Added full deferred loading semantic to precompiled/--noopt/eager-loading code (some corner cases, e.g., compile-time errors for constants, still missing)

BUG=
R=hausner@google.com

Review URL: https://codereview.chromium.org//1211273011 .
This commit is contained in:
Srdjan Mitrovic 2015-07-09 10:56:55 -07:00
parent e5e3d161e7
commit d503d2f0ed
13 changed files with 169 additions and 32 deletions

View file

@ -57,6 +57,18 @@ class _TypeError extends _AssertionError implements TypeError {
String error_msg)
native "TypeError_throwNew";
static _throwNewIfNotLoaded(_LibraryPrefix prefix,
int location,
Object src_value,
String dst_type_name,
String dst_name,
String error_msg) {
if (!prefix.isLoaded()) {
_throwNew(location, src_value, dst_type_name, dst_name, error_msg);
}
}
String toString() {
String str = (_errorMsg != null) ? _errorMsg : "";
if ((_dstName != null) && (_dstName.length > 0)) {
@ -181,6 +193,19 @@ patch class NoSuchMethodError {
existingArgumentNames);
}
static void _throwNewIfNotLoaded(_LibraryPrefix prefix,
Object receiver,
String memberName,
int invocation_type,
List arguments,
List argumentNames,
List existingArgumentNames) {
if (!prefix.isLoaded()) {
_throwNew(receiver, memberName, invocation_type, arguments,
argumentNames, existingArgumentNames);
}
}
// Remember the type from the invocation mirror or static compilation
// analysis when thrown directly with _throwNew. A negative value means
// that no information is available.

View file

@ -10,6 +10,7 @@ class _LibraryPrefix {
bool _load() native "LibraryPrefix_load";
Error _loadError() native "LibraryPrefix_loadError";
bool isLoaded() native "LibraryPrefix_isLoaded";
bool _invalidateDependentCode()
native "LibraryPrefix_invalidateDependentCode";

View file

@ -378,6 +378,13 @@ DEFINE_NATIVE_ENTRY(LibraryPrefix_loadError, 1) {
}
DEFINE_NATIVE_ENTRY(LibraryPrefix_isLoaded, 1) {
const LibraryPrefix& prefix =
LibraryPrefix::CheckedHandle(arguments->NativeArgAt(0));
return Bool::Get(prefix.is_loaded()).raw();
}
DEFINE_NATIVE_ENTRY(Internal_inquireIs64Bit, 0) {
#if defined(ARCH_IS_64_BIT)
return Bool::True().raw();

View file

@ -380,6 +380,7 @@ namespace dart {
V(LibraryPrefix_load, 1) \
V(LibraryPrefix_invalidateDependentCode, 1) \
V(LibraryPrefix_loadError, 1) \
V(LibraryPrefix_isLoaded, 1) \
V(UserTag_new, 2) \
V(UserTag_label, 1) \
V(UserTag_defaultTag, 0) \

View file

@ -64,6 +64,7 @@ DEFINE_FLAG(bool, use_inlining, true, "Enable call-site inlining");
DEFINE_FLAG(bool, verify_compiler, false,
"Enable compiler verification assertions");
DECLARE_FLAG(bool, load_deferred_eagerly);
DECLARE_FLAG(bool, trace_failed_optimization_attempts);
DECLARE_FLAG(bool, trace_inlining_intervals);
DECLARE_FLAG(bool, trace_irregexp);
@ -71,6 +72,7 @@ DECLARE_FLAG(bool, trace_patching);
bool Compiler::always_optimize_ = false;
bool Compiler::allow_recompilation_ = true;
// TODO(zerny): Factor out unoptimizing/optimizing pipelines and remove
@ -788,6 +790,7 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
ASSERT(CodePatcher::CodeIsPatchable(code));
}
if (parsed_function->HasDeferredPrefixes()) {
ASSERT(!FLAG_load_deferred_eagerly);
ZoneGrowableArray<const LibraryPrefix*>* prefixes =
parsed_function->deferred_prefixes();
for (intptr_t i = 0; i < prefixes->length(); i++) {
@ -972,6 +975,11 @@ static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
const Function& function,
bool optimized,
intptr_t osr_id) {
// Check that we optimize if 'Compiler::always_optimize()' is set to true,
// except if the function is marked as not optimizable.
ASSERT(!function.IsOptimizable() ||
!Compiler::always_optimize() || optimized);
ASSERT(Compiler::allow_recompilation() || !function.HasCode());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
Thread* const thread = Thread::Current();

View file

@ -90,8 +90,14 @@ class Compiler : public AllStatic {
static bool always_optimize() { return always_optimize_; }
static void set_always_optimize(bool value) { always_optimize_ = value; }
static bool allow_recompilation() { return allow_recompilation_; }
static void set_allow_recompilation(bool value) {
allow_recompilation_ = value;
}
private:
static bool always_optimize_;
static bool allow_recompilation_;
};
} // namespace dart

View file

@ -81,10 +81,14 @@ static void NooptModeHandler(bool value) {
FLAG_collect_code = false;
FLAG_load_deferred_eagerly = true;
FLAG_deoptimize_alot = false; // Used in some tests.
FLAG_deoptimize_every = 0; // Used in some tests.
FLAG_deoptimize_every = 0; // Used in some tests.
FLAG_collect_code = false;
FLAG_guess_other_cid = true;
Compiler::set_always_optimize(true);
// Triggers assert if we try to recompile (e.g., because of deferred
// loading, deoptimization, ...). Noopt mode simulates behavior
// of precompiled code, therefore do not allow recompilation.
Compiler::set_allow_recompilation(false);
// TODO(srdjan): Enable CHA deoptimization when eager class finalization is
// implemented, either with precompilation or as a special pass.
FLAG_use_cha_deopt = false;

View file

@ -66,13 +66,15 @@ DEFINE_FLAG(bool, use_field_guards, true, "Guard field cids.");
DEFINE_FLAG(bool, use_lib_cache, true, "Use library name cache");
DEFINE_FLAG(bool, trace_field_guards, false, "Trace changes in field's cids.");
DECLARE_FLAG(charp, coverage_dir);
DECLARE_FLAG(bool, load_deferred_eagerly);
DECLARE_FLAG(bool, show_invisible_frames);
DECLARE_FLAG(bool, trace_compiler);
DECLARE_FLAG(bool, trace_deoptimization);
DECLARE_FLAG(bool, trace_deoptimization_verbose);
DECLARE_FLAG(bool, show_invisible_frames);
DECLARE_FLAG(charp, coverage_dir);
DECLARE_FLAG(bool, write_protect_code);
static const char* kGetterPrefix = "get:";
static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix);
static const char* kSetterPrefix = "set:";
@ -10219,7 +10221,7 @@ void LibraryPrefix::AddImport(const Namespace& import) const {
RawObject* LibraryPrefix::LookupObject(const String& name) const {
if (!is_loaded()) {
if (!is_loaded() && !FLAG_load_deferred_eagerly) {
return Object::null();
}
Array& imports = Array::Handle(this->imports());

View file

@ -38,15 +38,17 @@
namespace dart {
DEFINE_FLAG(bool, enable_debug_break, false, "Allow use of break \"message\".");
DEFINE_FLAG(bool, enable_mirrors, true,
"Disable to make importing dart:mirrors an error.");
DEFINE_FLAG(bool, load_deferred_eagerly, false,
"Load deferred libraries eagerly.");
DEFINE_FLAG(bool, trace_parser, false, "Trace parser operations.");
DEFINE_FLAG(bool, warn_mixin_typedef, true, "Warning on legacy mixin typedef.");
DECLARE_FLAG(bool, lazy_dispatchers);
DECLARE_FLAG(bool, load_deferred_eagerly);
DECLARE_FLAG(bool, throw_on_javascript_int_overflow);
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
DEFINE_FLAG(bool, enable_mirrors, true,
"Disable to make importing dart:mirrors an error.");
DECLARE_FLAG(bool, lazy_dispatchers);
// Quick access to the current isolate and zone.
#define I (isolate())
@ -172,6 +174,9 @@ void ParsedFunction::SetRegExpCompileData(
void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
// 'deferred_prefixes_' are used to invalidate code, but no invalidation is
// needed if --load_deferred_eagerly.
ASSERT(!FLAG_load_deferred_eagerly);
ASSERT(prefix.is_deferred_load());
ASSERT(!prefix.is_loaded());
for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
@ -10038,8 +10043,19 @@ SequenceNode* Parser::NodeAsSequenceNode(intptr_t sequence_pos,
}
AstNode* Parser::ThrowTypeError(intptr_t type_pos, const AbstractType& type) {
// Call _throwNewIfNotLoaded if prefix is not NULL, otherwise call _throwNew.
AstNode* Parser::ThrowTypeError(intptr_t type_pos, const AbstractType& type,
LibraryPrefix* prefix) {
ArgumentListNode* arguments = new(Z) ArgumentListNode(type_pos);
String& method_name = String::Handle(Z);
if (prefix == NULL) {
method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw();
} else {
arguments->Add(new(Z) LiteralNode(type_pos, *prefix));
method_name = Library::PrivateCoreLibName(
Symbols::ThrowNewIfNotLoaded()).raw();
}
// Location argument.
arguments->Add(new(Z) LiteralNode(
type_pos, Integer::ZoneHandle(Z, Integer::New(type_pos))));
@ -10054,20 +10070,29 @@ AstNode* Parser::ThrowTypeError(intptr_t type_pos, const AbstractType& type) {
ASSERT(!error.IsNull());
arguments->Add(new(Z) LiteralNode(type_pos, String::ZoneHandle(Z,
Symbols::New(error.ToErrorCString()))));
return MakeStaticCall(Symbols::TypeError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
arguments);
return MakeStaticCall(Symbols::TypeError(), method_name, arguments);
}
// Call _throwNewIfNotLoaded if prefix is not NULL, otherwise call _throwNew.
AstNode* Parser::ThrowNoSuchMethodError(intptr_t call_pos,
const Class& cls,
const String& function_name,
ArgumentListNode* function_arguments,
InvocationMirror::Call im_call,
InvocationMirror::Type im_type,
const Function* func) {
const Function* func,
const LibraryPrefix* prefix) {
ArgumentListNode* arguments = new(Z) ArgumentListNode(call_pos);
String& method_name = String::Handle(Z);
if (prefix == NULL) {
method_name = Library::PrivateCoreLibName(Symbols::ThrowNew()).raw();
} else {
arguments->Add(new(Z) LiteralNode(call_pos, *prefix));
method_name = Library::PrivateCoreLibName(
Symbols::ThrowNewIfNotLoaded()).raw();
}
// Object receiver.
// If the function is external and dynamic, pass the actual receiver,
// otherwise, pass a class literal of the unresolved method's owner.
@ -10140,9 +10165,7 @@ AstNode* Parser::ThrowNoSuchMethodError(intptr_t call_pos,
}
arguments->Add(new(Z) LiteralNode(call_pos, array));
return MakeStaticCall(Symbols::NoSuchMethodError(),
Library::PrivateCoreLibName(Symbols::ThrowNew()),
arguments);
return MakeStaticCall(Symbols::NoSuchMethodError(), method_name, arguments);
}
@ -11816,7 +11839,7 @@ AstNode* Parser::ResolveIdentInPrefixScope(intptr_t ident_pos,
return NULL;
}
Object& obj = Object::Handle(Z);
if (prefix.is_loaded()) {
if (prefix.is_loaded() || FLAG_load_deferred_eagerly) {
obj = prefix.LookupObject(ident);
} else {
// Remember that this function depends on an import prefix of an
@ -11952,16 +11975,25 @@ AstNode* Parser::ResolveIdent(intptr_t ident_pos,
}
// Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and
// finalize it according to the given type finalization mode.
RawAbstractType* Parser::ParseType(
ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type,
bool consume_unresolved_prefix) {
LibraryPrefix& prefix = LibraryPrefix::Handle(Z);
return ParseType(finalization, allow_deferred_type,
consume_unresolved_prefix, &prefix);
}
// Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and
// finalize it according to the given type finalization mode. Returns prefix.
RawAbstractType* Parser::ParseType(
ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type,
bool consume_unresolved_prefix,
LibraryPrefix* prefix) {
TRACE_PARSER("ParseType");
CheckToken(Token::kIDENT, "type name expected");
intptr_t ident_pos = TokenPos();
LibraryPrefix& prefix = LibraryPrefix::Handle(Z);
String& type_name = String::Handle(Z);
if (finalization == ClassFinalizer::kIgnore) {
@ -11972,7 +12004,7 @@ RawAbstractType* Parser::ParseType(
}
SkipQualIdent();
} else {
prefix = ParsePrefix();
*prefix = ParsePrefix();
type_name = CurrentLiteral()->raw();
ConsumeToken();
@ -11982,7 +12014,7 @@ RawAbstractType* Parser::ParseType(
// a period and another identifier, consume the qualified identifier
// and create a malformed type.
if (consume_unresolved_prefix &&
prefix.IsNull() &&
prefix->IsNull() &&
(CurrentToken() == Token::kPERIOD) &&
(Token::IsIdentifier(LookaheadToken(1)))) {
if (!is_top_level_ && (current_block_ != NULL)) {
@ -12009,7 +12041,7 @@ RawAbstractType* Parser::ParseType(
// If parsing inside a local scope, check whether the type name
// is shadowed by a local declaration.
if (!is_top_level_ &&
(prefix.IsNull()) &&
(prefix->IsNull()) &&
ResolveIdentInLocalScope(ident_pos, type_name, NULL)) {
// The type is malformed. Skip over its type arguments.
ParseTypeArguments(ClassFinalizer::kIgnore);
@ -12020,29 +12052,30 @@ RawAbstractType* Parser::ParseType(
"using '%s' in this context is invalid",
type_name.ToCString());
}
if (!prefix.IsNull() && prefix.is_deferred_load()) {
if (!FLAG_load_deferred_eagerly &&
!prefix->IsNull() && prefix->is_deferred_load()) {
// If deferred prefixes are allowed but it is not yet loaded,
// remember that this function depends on the prefix.
if (allow_deferred_type && !prefix.is_loaded()) {
if (allow_deferred_type && !prefix->is_loaded()) {
if (parsed_function() != NULL) {
parsed_function()->AddDeferredPrefix(prefix);
parsed_function()->AddDeferredPrefix(*prefix);
}
}
// If the deferred prefixes are not allowed, or if the prefix is not yet
// loaded when finalization is requested, return a malformed type.
// Otherwise, handle resolution below, as needed.
if (!allow_deferred_type ||
(!prefix.is_loaded()
(!prefix->is_loaded()
&& (finalization > ClassFinalizer::kResolveTypeParameters))) {
ParseTypeArguments(ClassFinalizer::kIgnore);
return ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_,
ident_pos,
!prefix.is_loaded()
!prefix->is_loaded()
? "deferred type '%s.%s' is not yet loaded"
: "using deferred type '%s.%s' is invalid",
String::Handle(Z, prefix.name()).ToCString(),
String::Handle(Z, prefix->name()).ToCString(),
type_name.ToCString());
}
}
@ -12050,7 +12083,7 @@ RawAbstractType* Parser::ParseType(
Object& type_class = Object::Handle(Z);
// Leave type_class as null if type finalization mode is kIgnore.
if (finalization != ClassFinalizer::kIgnore) {
type_class = UnresolvedClass::New(prefix, type_name, ident_pos);
type_class = UnresolvedClass::New(*prefix, type_name, ident_pos);
}
TypeArguments& type_arguments = TypeArguments::Handle(
Z, ParseTypeArguments(finalization));
@ -12599,10 +12632,28 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) {
const bool allow_deferred_type = !is_const;
const bool consume_unresolved_prefix = (LookaheadToken(3) == Token::kLT) ||
(LookaheadToken(3) == Token::kPERIOD);
LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(Z);
AbstractType& type = AbstractType::Handle(Z,
ParseType(ClassFinalizer::kCanonicalizeWellFormed,
allow_deferred_type,
consume_unresolved_prefix));
consume_unresolved_prefix,
&prefix));
if (FLAG_load_deferred_eagerly &&
!prefix.IsNull() && prefix.is_deferred_load() && !prefix.is_loaded()) {
// Add runtime check.
Type& malformed_type = Type::Handle(Z);
malformed_type = ClassFinalizer::NewFinalizedMalformedType(
Error::Handle(Z), // No previous error.
script_,
type_pos,
"deferred type '%s.%s' is not yet loaded",
String::Handle(Z, prefix.name()).ToCString(),
String::Handle(type.Name()).ToCString());
// Note: Adding a statement to current block is a hack, parsing an
// expression should have no side-effect.
current_block_->statements->Add(
ThrowTypeError(type_pos, malformed_type, &prefix));
}
// In case the type is malformed, throw a dynamic type error after finishing
// parsing the instance creation expression.
if (!type.IsMalformed() && (type.IsTypeParameter() || type.IsDynamicType())) {
@ -13083,6 +13134,26 @@ AstNode* Parser::ParsePrimary() {
call_type,
NULL); // No existing function.
}
} else if (FLAG_load_deferred_eagerly && prefix.is_deferred_load()) {
// primary != NULL.
String& qualified_name = String::ZoneHandle(Z, prefix.name());
qualified_name = String::Concat(qualified_name, Symbols::Dot());
qualified_name = String::Concat(qualified_name, ident);
qualified_name = Symbols::New(qualified_name);
InvocationMirror::Type call_type =
CurrentToken() == Token::kLPAREN ?
InvocationMirror::kMethod : InvocationMirror::kGetter;
// Note: Adding a statement to current block is a hack, parsing an
// espression should have no side-effect.
current_block_->statements->Add(ThrowNoSuchMethodError(
qual_ident_pos,
current_class(),
qualified_name,
NULL, // No arguments.
InvocationMirror::kTopLevel,
call_type,
NULL, // No existing function.
&prefix));
}
}
ASSERT(primary != NULL);

View file

@ -432,6 +432,12 @@ class Parser : public ValueObject {
RawAbstractType* ParseType(ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type = false,
bool consume_unresolved_prefix = true);
RawAbstractType* ParseType(
ClassFinalizer::FinalizationKind finalization,
bool allow_deferred_type,
bool consume_unresolved_prefix,
LibraryPrefix* prefix);
void ParseTypeParameters(const Class& cls);
RawTypeArguments* ParseTypeArguments(
ClassFinalizer::FinalizationKind finalization);
@ -763,14 +769,16 @@ class Parser : public ValueObject {
ArgumentListNode* arguments);
String& Interpolate(const GrowableArray<AstNode*>& values);
AstNode* MakeAssertCall(intptr_t begin, intptr_t end);
AstNode* ThrowTypeError(intptr_t type_pos, const AbstractType& type);
AstNode* ThrowTypeError(intptr_t type_pos, const AbstractType& type,
LibraryPrefix* prefix = NULL);
AstNode* ThrowNoSuchMethodError(intptr_t call_pos,
const Class& cls,
const String& function_name,
ArgumentListNode* function_arguments,
InvocationMirror::Call call,
InvocationMirror::Type type,
const Function* func);
const Function* func,
const LibraryPrefix* prefix = NULL);
void SetupSavedTryContext(LocalVariable* saved_try_context);

View file

@ -66,6 +66,7 @@ class ObjectPointerVisitor;
V(NoSuchMethodError, "NoSuchMethodError") \
V(CyclicInitializationError, "CyclicInitializationError") \
V(ThrowNew, "_throwNew") \
V(ThrowNewIfNotLoaded, "_throwNewIfNotLoaded") \
V(Symbol, "Symbol") \
V(SymbolCtor, "Symbol.") \
V(List, "List") \

View file

@ -8,6 +8,7 @@
#include "vm/code_generator.h"
#include "vm/code_patcher.h"
#include "vm/compiler.h"
#include "vm/object.h"
#include "vm/stack_frame.h"
@ -60,6 +61,7 @@ void WeakCodeReferences::DisableCode() {
if (code_objects.IsNull()) {
return;
}
ASSERT(Compiler::allow_recompilation());
UpdateArrayTo(Object::null_array());
// Disable all code on stack.
Code& code = Code::Handle();

View file

@ -14,6 +14,7 @@ import "deferred_static_seperate_lib2.dart" deferred as lib2;
void main() {
asyncStart();
Expect.throws(() => new lib1.C());
lib1.loadLibrary().then((_) {
lib2.loadLibrary().then((_) {
print("HERE");