Index: runtime/vm/parser.cc |
=================================================================== |
--- runtime/vm/parser.cc (revision 34480) |
+++ runtime/vm/parser.cc (working copy) |
@@ -127,6 +127,22 @@ |
} |
+void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) { |
+ ASSERT(prefix.is_deferred_load()); |
+ ASSERT(!prefix.is_loaded()); |
+ if (deferred_prefixes_ == NULL) { |
+ deferred_prefixes_ = |
+ &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New()); |
+ } |
+ for (intptr_t i = 0; i < deferred_prefixes_->Length(); i++) { |
+ if (deferred_prefixes_->At(i) == prefix.raw()) { |
+ return; |
+ } |
+ } |
+ deferred_prefixes_->Add(prefix); |
+} |
+ |
+ |
void ParsedFunction::AllocateVariables() { |
LocalScope* scope = node_sequence()->scope(); |
const intptr_t num_fixed_params = function().num_fixed_parameters(); |
@@ -4945,19 +4961,32 @@ |
if (url.Length() == 0) { |
ErrorMsg("library url expected"); |
} |
+ bool is_deferred_import = false; |
+ if (is_import && (IsLiteral("deferred"))) { |
+ is_deferred_import = true; |
+ ConsumeToken(); |
+ CheckToken(Token::kAS, "'as' expected"); |
+ } |
String& prefix = String::Handle(); |
+ intptr_t prefix_pos = 0; |
if (is_import && (CurrentToken() == Token::kAS)) { |
ConsumeToken(); |
+ prefix_pos = TokenPos(); |
prefix = ExpectIdentifier("prefix identifier expected")->raw(); |
} |
Array& show_names = Array::Handle(); |
Array& hide_names = Array::Handle(); |
- if (IsLiteral("show") || IsLiteral("hide")) { |
+ if (is_deferred_import || IsLiteral("show") || IsLiteral("hide")) { |
GrowableObjectArray& show_list = |
GrowableObjectArray::Handle(GrowableObjectArray::New()); |
GrowableObjectArray& hide_list = |
GrowableObjectArray::Handle(GrowableObjectArray::New()); |
+ // Libraries imported through deferred import automatically hide |
+ // the name 'loadLibrary'. |
+ if (is_deferred_import) { |
+ hide_list.Add(Symbols::LoadLibrary()); |
+ } |
for (;;) { |
if (IsLiteral("show")) { |
ConsumeToken(); |
@@ -4985,6 +5014,7 @@ |
Library& library = Library::Handle(Library::LookupLibrary(canon_url)); |
if (library.IsNull()) { |
// Call the library tag handler to load the library. |
+ // TODO(hausner): do not load eagerly if import is deferred. |
CallLibraryTagHandler(Dart_kImportTag, import_pos, canon_url); |
// If the library tag handler succeded without registering the |
// library we create an empty library to import. |
@@ -5010,14 +5040,25 @@ |
ErrorMsg(import_pos, "private library is not accessible"); |
} |
if (prefix.IsNull() || (prefix.Length() == 0)) { |
+ ASSERT(!is_deferred_import); |
library_.AddImport(ns); |
} else { |
LibraryPrefix& library_prefix = LibraryPrefix::Handle(); |
library_prefix = library_.LookupLocalLibraryPrefix(prefix); |
if (!library_prefix.IsNull()) { |
+ // Check that prefix names of deferred import clauses are |
+ // unique. |
+ if (!is_deferred_import && library_prefix.is_deferred_load()) { |
+ ErrorMsg(prefix_pos, |
+ "prefix '%s' already used in a deferred import clause", |
+ prefix.ToCString()); |
+ } |
+ if (is_deferred_import) { |
+ ErrorMsg(prefix_pos, "prefix of deferred import must be uniqe"); |
+ } |
library_prefix.AddImport(ns); |
} else { |
- library_prefix = LibraryPrefix::New(prefix, ns); |
+ library_prefix = LibraryPrefix::New(prefix, ns, is_deferred_import); |
library_.AddObject(library_prefix, prefix); |
} |
} |
@@ -9196,14 +9237,17 @@ |
const String& ident) { |
TRACE_PARSER("ResolveIdentInPrefixScope"); |
HANDLESCOPE(isolate()); |
- Object& obj = Object::Handle(prefix.LookupObject(ident)); |
+ Object& obj = Object::Handle(); |
+ if (prefix.is_loaded()) { |
+ obj = prefix.LookupObject(ident); |
+ } else { |
+ // Remember that this function depends on an import prefix of an |
+ // unloaded deferred library. |
+ parsed_function()->AddDeferredPrefix(prefix); |
+ } |
if (obj.IsNull()) { |
// Unresolved prefixed primary identifier. |
- String& qualified_name = String::ZoneHandle(prefix.name()); |
- qualified_name = String::Concat(qualified_name, Symbols::Dot()); |
- qualified_name = String::Concat(qualified_name, ident); |
- qualified_name = Symbols::New(qualified_name); |
- return new PrimaryNode(ident_pos, qualified_name); |
+ return NULL; |
} else if (obj.IsClass()) { |
const Class& cls = Class::Cast(obj); |
return new PrimaryNode(ident_pos, Class::ZoneHandle(cls.raw())); |
@@ -9311,7 +9355,8 @@ |
// Parses type = [ident "."] ident ["<" type { "," type } ">"], then resolve and |
// finalize it according to the given type finalization mode. |
RawAbstractType* Parser::ParseType( |
- ClassFinalizer::FinalizationKind finalization) { |
+ ClassFinalizer::FinalizationKind finalization, |
+ bool allow_deferred_type) { |
TRACE_PARSER("ParseType"); |
CheckToken(Token::kIDENT, "type name expected"); |
QualIdent type_name; |
@@ -9337,6 +9382,18 @@ |
"using '%s' in this context is invalid", |
type_name.ident->ToCString()); |
} |
+ if ((type_name.lib_prefix != NULL) && |
+ type_name.lib_prefix->is_deferred_load() && |
+ !allow_deferred_type) { |
+ ParseTypeArguments(ClassFinalizer::kIgnore); |
+ return ClassFinalizer::NewFinalizedMalformedType( |
+ Error::Handle(), // No previous error. |
+ script_, |
+ type_name.ident_pos, |
+ "using deferred type '%s.%s' is invalid", |
+ String::Handle(type_name.lib_prefix->name()).ToCString(), |
+ type_name.ident->ToCString()); |
+ } |
} |
Object& type_class = Object::Handle(isolate()); |
// Leave type_class as null if type finalization mode is kIgnore. |
@@ -9895,8 +9952,10 @@ |
ErrorMsg("type name expected"); |
} |
intptr_t type_pos = TokenPos(); |
+ // Can't allocate const objects of a deferred type. |
+ const bool allow_deferred_type = !is_const; |
AbstractType& type = AbstractType::Handle( |
- ParseType(ClassFinalizer::kCanonicalizeWellFormed)); |
+ ParseType(ClassFinalizer::kCanonicalizeWellFormed, allow_deferred_type)); |
// 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())) { |
@@ -10282,6 +10341,7 @@ |
CloseBlock(); |
} else if (IsIdentifier()) { |
QualIdent qual_ident; |
+ intptr_t qual_ident_pos = TokenPos(); |
ParseQualIdent(&qual_ident); |
if (qual_ident.lib_prefix == NULL) { |
if (!ResolveIdentInLocalScope(qual_ident.ident_pos, |
@@ -10312,30 +10372,42 @@ |
// Note: unlike in the case of an unqualified identifier, do not |
// interpret the unresolved identifier as an instance method or |
// instance getter call when compiling an instance method. |
- // TODO(hausner): Ideally we should generate the NoSuchMethodError |
- // later, when we know more about how the unresolved name is used. |
- // For example, we don't know yet whether the unresolved name |
- // refers to a getter or a setter. However, it is more awkward |
- // to distinuish four NoSuchMethodError cases all over the place |
- // in the parser. The four cases are: prefixed vs non-prefixed |
- // name, static vs dynamic context in which the unresolved name |
- // is used. We cheat a little here by looking at the next token |
- // to determine whether we have an unresolved method call or |
- // field access. |
- if (primary->IsPrimaryNode() && |
- primary->AsPrimaryNode()->primary().IsString()) { |
- InvocationMirror::Type call_type = |
- CurrentToken() == Token::kLPAREN ? |
- InvocationMirror::kMethod : InvocationMirror::kGetter; |
- const String& unresolved_name = |
- String::Cast(primary->AsPrimaryNode()->primary()); |
- primary = ThrowNoSuchMethodError(primary->token_pos(), |
- current_class(), |
- unresolved_name, |
- NULL, // No arguments. |
- InvocationMirror::kTopLevel, |
- call_type, |
- NULL); // No existing function. |
+ if (primary == NULL) { |
+ if (qual_ident.lib_prefix->is_deferred_load() && |
+ qual_ident.ident->Equals(Symbols::LoadLibrary())) { |
+ // Hack Alert: recognize special 'loadLibrary' call on the |
+ // prefix object. The prefix is the primary. Rewind parser and |
+ // let ParseSelectors() handle the loadLibrary call. |
+ SetPosition(qual_ident_pos); |
+ ConsumeToken(); // Prefix name. |
+ primary = new LiteralNode(qual_ident_pos, *qual_ident.lib_prefix); |
+ } else { |
+ // TODO(hausner): Ideally we should generate the NoSuchMethodError |
+ // later, when we know more about how the unresolved name is used. |
+ // For example, we don't know yet whether the unresolved name |
+ // refers to a getter or a setter. However, it is more awkward |
+ // to distinuish four NoSuchMethodError cases all over the place |
+ // in the parser. The four cases are: prefixed vs non-prefixed |
+ // name, static vs dynamic context in which the unresolved name |
+ // is used. We cheat a little here by looking at the next token |
+ // to determine whether we have an unresolved method call or |
+ // field access. |
+ String& qualified_name = |
+ String::ZoneHandle(qual_ident.lib_prefix->name()); |
+ qualified_name = String::Concat(qualified_name, Symbols::Dot()); |
+ qualified_name = String::Concat(qualified_name, *qual_ident.ident); |
+ qualified_name = Symbols::New(qualified_name); |
+ InvocationMirror::Type call_type = |
+ CurrentToken() == Token::kLPAREN ? |
+ InvocationMirror::kMethod : InvocationMirror::kGetter; |
+ primary = ThrowNoSuchMethodError(qual_ident_pos, |
+ current_class(), |
+ qualified_name, |
+ NULL, // No arguments. |
+ InvocationMirror::kTopLevel, |
+ call_type, |
+ NULL); // No existing function. |
+ } |
} |
} |
ASSERT(primary != NULL); |