Version 1.9.0-dev.10.4
svn merge -c 44263 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44265 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44267 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44287 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44288 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44290 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44295 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44297 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44299 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44300 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44301 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44302 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 44303 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
git-svn-id: http://dart.googlecode.com/svn/trunk@44314 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/lib/src/get_handler.dart b/pkg/analysis_server/lib/src/get_handler.dart
index aa4c2a2..d4f6a7a 100644
--- a/pkg/analysis_server/lib/src/get_handler.dart
+++ b/pkg/analysis_server/lib/src/get_handler.dart
@@ -686,8 +686,8 @@
implicitNames.sort();
_overlayContents.clear();
- context.visitContentCache((Source source, int stamp, String contents) {
- _overlayContents[source.fullName] = contents;
+ context.visitContentCache((String fullName, int stamp, String contents) {
+ _overlayContents[fullName] = contents;
});
void _writeFiles(
@@ -887,14 +887,13 @@
buffer.write('<table border="1">');
_overlayContents.clear();
ContentCache overlayState = analysisServer.overlayState;
- overlayState.accept((Source source, int stamp, String contents) {
- String fileName = source.fullName;
+ overlayState.accept((String fullName, int stamp, String contents) {
buffer.write('<tr>');
String link =
- makeLink(OVERLAY_PATH, {PATH_PARAM: fileName}, fileName);
+ makeLink(OVERLAY_PATH, {PATH_PARAM: fullName}, fullName);
DateTime time = new DateTime.fromMillisecondsSinceEpoch(stamp);
_writeRow(buffer, [link, time]);
- _overlayContents[fileName] = contents;
+ _overlayContents[fullName] = contents;
});
int count = _overlayContents.length;
buffer.write('<tr><td colspan="2">Total: $count entries.</td></tr>');
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index 2fdf505..4a45619 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -1189,12 +1189,6 @@
_updateEndOld = endOffsetOld;
_updateEndNew = endOffsetNew;
_updateDelta = newUnit.length - _oldUnit.length;
- // A comment change.
- if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
- bool success = _resolveComment(newUnit, firstPair);
- logger.log('Comment change: $success');
- return success;
- }
// A Dart documentation comment change.
if (firstPair.kind == _TokenDifferenceKind.COMMENT_DOC) {
bool success = _resolveCommentDoc(newUnit, firstPair);
@@ -1254,6 +1248,13 @@
logger.log('Failure: no enclosing function body or executable.');
return false;
}
+ // fail if a comment change outside the bodies
+ if (firstPair.kind == _TokenDifferenceKind.COMMENT) {
+ if (beginOffsetOld <= oldNode.offset || beginOffsetNew <= newNode.offset) {
+ logger.log('Failure: comment outside a function body.');
+ return false;
+ }
+ }
}
logger.log(() => 'oldNode: $oldNode');
logger.log(() => 'newNode: $newNode');
@@ -1315,28 +1316,6 @@
}
/**
- * Attempts to resolve a comment change.
- * Returns `true` if success.
- */
- bool _resolveComment(CompilationUnit newUnit, _TokenPair firstPair) {
- Token oldToken = firstPair.oldToken;
- Token newToken = firstPair.newToken;
- CommentToken newComments = newToken.precedingComments;
- // update token references
- _updateOffset = oldToken.offset - 1;
- _shiftTokens(firstPair.oldToken);
- _setPrecedingComments(oldToken, newComments);
- // update elements
- IncrementalResolver incrementalResolver = new IncrementalResolver(
- _unitElement, _updateOffset, _updateEndOld, _updateEndNew);
- incrementalResolver._updateElementNameOffsets();
- incrementalResolver._shiftEntryErrors();
- _updateEntry();
- // OK
- return true;
- }
-
- /**
* Attempts to resolve a documentation comment change.
* Returns `true` if success.
*/
@@ -1390,24 +1369,8 @@
token.precedingComments = comment;
} else if (token is KeywordTokenWithComment) {
token.precedingComments = comment;
- } else if (token is KeywordToken) {
- KeywordTokenWithComment newToken =
- new KeywordTokenWithComment(token.keyword, token.offset, comment);
- token.previous.setNext(newToken);
- newToken.setNext(token.next);
- if (_oldUnit.beginToken == token) {
- _oldUnit.beginToken = newToken;
- }
} else if (token is StringTokenWithComment) {
token.precedingComments = comment;
- } else if (token is StringToken) {
- StringTokenWithComment newToken = new StringTokenWithComment(
- token.type, token.value(), token.offset, comment);
- token.previous.setNext(newToken);
- newToken.setNext(token.next);
- if (_oldUnit.beginToken == token) {
- _oldUnit.beginToken = newToken;
- }
} else if (token is TokenWithComment) {
token.precedingComments = comment;
} else {
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index e491d91..e918ed8 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -2444,6 +2444,12 @@
// class that was parsed.
_parseClassDeclaration(commentAndMetadata, getAndAdvance());
return null;
+ } else if (_matchesKeyword(Keyword.ENUM)) {
+ _reportErrorForToken(ParserErrorCode.ENUM_IN_CLASS, _peek());
+ // TODO(brianwilkerson) We don't currently have any way to capture the
+ // enum that was parsed.
+ _parseEnumDeclaration(commentAndMetadata);
+ return null;
} else if (_isOperator(_currentToken)) {
//
// We appear to have found an operator declaration without the
@@ -9320,6 +9326,9 @@
static const ParserErrorCode EMPTY_ENUM_BODY = const ParserErrorCode(
'EMPTY_ENUM_BODY', "An enum must declare at least one constant name");
+ static const ParserErrorCode ENUM_IN_CLASS = const ParserErrorCode(
+ 'ENUM_IN_CLASS', "Enums cannot be declared inside classes");
+
static const ParserErrorCode EQUALITY_CANNOT_BE_EQUALITY_OPERAND =
const ParserErrorCode('EQUALITY_CANNOT_BE_EQUALITY_OPERAND',
"Equality expression cannot be operand of another equality expression.");
diff --git a/pkg/analyzer/lib/src/generated/source.dart b/pkg/analyzer/lib/src/generated/source.dart
index 805897f..d0b5dde 100644
--- a/pkg/analyzer/lib/src/generated/source.dart
+++ b/pkg/analyzer/lib/src/generated/source.dart
@@ -22,86 +22,78 @@
import 'source_io.dart' show FileBasedSource;
/**
- * A function that is used to handle [ContentCache] entries.
+ * A function that is used to visit [ContentCache] entries.
*/
-typedef void ContentCacheVisitor(Source source, int stamp, String contents);
+typedef void ContentCacheVisitor(String fullPath, int stamp, String contents);
/**
- * Instances of class `ContentCache` hold content used to override the default content of a
- * [Source].
+ * A cache used to override the default content of a [Source].
*/
class ContentCache {
/**
- * A table mapping sources to the contents of those sources. This is used to override the default
- * contents of a source.
+ * A table mapping the full path of sources to the contents of those sources.
+ * This is used to override the default contents of a source.
*/
- HashMap<Source, String> _contentMap = new HashMap<Source, String>();
+ HashMap<String, String> _contentMap = new HashMap<String, String>();
/**
- * A table mapping sources to the modification stamps of those sources. This is used when the
- * default contents of a source has been overridden.
+ * A table mapping the full path of sources to the modification stamps of
+ * those sources. This is used when the default contents of a source has been
+ * overridden.
*/
- HashMap<Source, int> _stampMap = new HashMap<Source, int>();
+ HashMap<String, int> _stampMap = new HashMap<String, int>();
/**
* Visit all entries of this cache.
*/
void accept(ContentCacheVisitor visitor) {
- _contentMap.forEach((Source source, String contents) {
- int stamp = _stampMap[source];
- visitor(source, stamp, contents);
+ _contentMap.forEach((String fullPath, String contents) {
+ int stamp = _stampMap[fullPath];
+ visitor(fullPath, stamp, contents);
});
}
/**
- * Return the contents of the given source, or `null` if this cache does not override the
- * contents of the source.
- *
- * <b>Note:</b> This method is not intended to be used except by
- * [AnalysisContext.getContents].
- *
- * @param source the source whose content is to be returned
- * @return the contents of the given source
- */
- String getContents(Source source) => _contentMap[source];
-
- /**
- * Return the modification stamp of the given source, or `null` if this cache does not
+ * Return the contents of the given [source], or `null` if this cache does not
* override the contents of the source.
*
* <b>Note:</b> This method is not intended to be used except by
- * [AnalysisContext.getModificationStamp].
- *
- * @param source the source whose modification stamp is to be returned
- * @return the modification stamp of the given source
+ * [AnalysisContext.getContents].
*/
- int getModificationStamp(Source source) => _stampMap[source];
+ String getContents(Source source) => _contentMap[source.fullName];
/**
- * Set the contents of the given source to the given contents. This has the effect of overriding
- * the default contents of the source. If the contents are `null` the override is removed so
- * that the default contents will be returned.
+ * Return the modification stamp of the given [source], or `null` if this
+ * cache does not override the contents of the source.
*
- * @param source the source whose contents are being overridden
- * @param contents the new contents of the source
- * @return the original cached contents or `null` if none
+ * <b>Note:</b> This method is not intended to be used except by
+ * [AnalysisContext.getModificationStamp].
+ */
+ int getModificationStamp(Source source) => _stampMap[source.fullName];
+
+ /**
+ * Set the contents of the given [source] to the given [contents]. This has
+ * the effect of overriding the default contents of the source. If the
+ * contents are `null` the override is removed so that the default contents
+ * will be returned.
*/
String setContents(Source source, String contents) {
+ String fullName = source.fullName;
if (contents == null) {
- _stampMap.remove(source);
- return _contentMap.remove(source);
+ _stampMap.remove(fullName);
+ return _contentMap.remove(fullName);
} else {
int newStamp = JavaSystem.currentTimeMillis();
- int oldStamp = _stampMap[source];
- _stampMap[source] = newStamp;
+ int oldStamp = _stampMap[fullName];
+ _stampMap[fullName] = newStamp;
// Occasionally, if this method is called in rapid succession, the
// timestamps are equal. Guard against this by artificially incrementing
// the new timestamp.
if (newStamp == oldStamp) {
- _stampMap[source] = newStamp + 1;
+ _stampMap[fullName] = newStamp + 1;
}
- String oldContent = _contentMap[source];
- _contentMap[source] = contents;
+ String oldContent = _contentMap[fullName];
+ _contentMap[fullName] = contents;
return oldContent;
}
}
diff --git a/pkg/analyzer/test/generated/incremental_resolver_test.dart b/pkg/analyzer/test/generated/incremental_resolver_test.dart
index c65b545..3eb51e3 100644
--- a/pkg/analyzer/test/generated/incremental_resolver_test.dart
+++ b/pkg/analyzer/test/generated/incremental_resolver_test.dart
@@ -2843,7 +2843,7 @@
''');
}
- void test_endOfLineComment_header_add() {
+ void test_endOfLineComment_outBody_add() {
_resolveUnit(r'''
main() {
Object x;
@@ -2856,10 +2856,10 @@
Object x;
x.foo();
}
-''');
+''', expectedSuccess: false);
}
- void test_endOfLineComment_header_remove() {
+ void test_endOfLineComment_outBody_remove() {
_resolveUnit(r'''
// 000
main() {
@@ -2872,10 +2872,10 @@
Object x;
x.foo();
}
-''');
+''', expectedSuccess: false);
}
- void test_endOfLineComment_header_update() {
+ void test_endOfLineComment_outBody_update() {
_resolveUnit(r'''
// 000
main() {
@@ -2889,7 +2889,7 @@
Object x;
x.foo();
}
-''');
+''', expectedSuccess: false);
}
void test_endOfLineComment_localFunction_inTopLevelVariable() {
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 524d0511..78c7934 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -917,6 +917,16 @@
], "enum E {}", [ParserErrorCode.EMPTY_ENUM_BODY]);
}
+ void test_enumInClass() {
+ ParserTestCase.parseCompilationUnit(r'''
+class Foo {
+ enum Bar {
+ Bar1, Bar2, Bar3
+ }
+}
+''', [ParserErrorCode.ENUM_IN_CLASS]);
+ }
+
void test_equalityCannotBeEqualityOperand_eq_eq() {
ParserTestCase.parseExpression(
"1 == 2 == 3", [ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND]);
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
index 1490ada..c2863c5 100644
--- a/pkg/compiler/lib/src/js/rewrite_async.dart
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -659,7 +659,8 @@
_makeVariableInitializer(handler, js.number(rethrowLabel)));
variables.add(_makeVariableInitializer(currentError, null));
if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) {
- variables.add(_makeVariableInitializer(next, null));
+ variables.add(_makeVariableInitializer(next,
+ new js.ArrayInitializer(<js.Expression>[])));
}
if (analysis.hasThis && !isSyncStar) {
// Sync* functions must remember `this` on the level of the outer
@@ -1416,7 +1417,7 @@
} else {
// The handler is reset as the first thing in the finally block.
addStatement(
- js.js.statement("# = [#];", [next, js.number(afterFinallyLabel)]));
+ js.js.statement("#.push(#);", [next, js.number(afterFinallyLabel)]));
addGoto(finallyLabel);
}
@@ -1440,8 +1441,9 @@
if (node.finallyPart != null) {
// The error has been caught, so after the finally, continue after the
// try.
- addStatement(js.js.statement("# = [#];",
- [next, js.number(afterFinallyLabel)]));
+ addStatement(
+ js.js.statement("#.push(#);",
+ [next, js.number(afterFinallyLabel)]));
addGoto(finallyLabel);
} else {
addGoto(afterFinallyLabel);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 7914cef..718a227 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -6084,6 +6084,21 @@
}
+bool Function::IsImplicitStaticClosureFunction(RawFunction* func) {
+ NoGCScope no_gc;
+ uint32_t kind_tag = func->ptr()->kind_tag_;
+ if (KindBits::decode(kind_tag) != RawFunction::kClosureFunction) {
+ return false;
+ }
+ if (!StaticBit::decode(kind_tag)) {
+ return false;
+ }
+ RawClosureData* data = reinterpret_cast<RawClosureData*>(func->ptr()->data_);
+ RawFunction* parent_function = data->ptr()->parent_function_;
+ return (parent_function->ptr()->data_ == reinterpret_cast<RawObject*>(func));
+}
+
+
RawFunction* Function::New() {
ASSERT(Object::function_class() != Class::null());
RawObject* raw = Object::Allocate(Function::kClassId,
@@ -6383,10 +6398,15 @@
RawInstance* Function::ImplicitStaticClosure() const {
if (implicit_static_closure() == Instance::null()) {
- ObjectStore* object_store = Isolate::Current()->object_store();
- const Context& context = Context::Handle(object_store->empty_context());
- const Instance& closure =
- Instance::Handle(Closure::New(*this, context, Heap::kOld));
+ Isolate* isolate = Isolate::Current();
+ ObjectStore* object_store = isolate->object_store();
+ const Context& context = Context::Handle(isolate,
+ object_store->empty_context());
+ Instance& closure =
+ Instance::Handle(isolate, Closure::New(*this, context, Heap::kOld));
+ const char* error_str = NULL;
+ closure ^= closure.CheckAndCanonicalize(&error_str);
+ ASSERT(!closure.IsNull());
set_implicit_static_closure(closure);
}
return implicit_static_closure();
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 806dbe3..b4002ce 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2135,6 +2135,7 @@
bool IsImplicitStaticClosureFunction() const {
return is_static() && IsImplicitClosureFunction();
}
+ bool static IsImplicitStaticClosureFunction(RawFunction* func);
// Returns true if this function represents an implicit instance closure
// function.
@@ -2377,6 +2378,7 @@
FINAL_HEAP_OBJECT_IMPLEMENTATION(Function, Object);
friend class Class;
+ friend class SnapshotWriter;
friend class Parser; // For set_eval_script.
// RawFunction::VisitFunctionPointers accesses the private constructor of
// Function.
@@ -7335,8 +7337,13 @@
// Indicates this class cannot be extended by dart code.
return -kWordSize;
}
+ static RawFunction* GetFunction(RawObject* obj) {
+ return *(reinterpret_cast<RawFunction**>(
+ reinterpret_cast<intptr_t>(obj->ptr()) + function_offset()));
+ }
friend class Class;
+ friend class SnapshotWriter;
};
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 9d145bb..f4a537f 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -542,7 +542,9 @@
friend class Array;
friend class ByteBuffer;
friend class Code;
+ friend class Closure;
friend class FreeListElement;
+ friend class Function;
friend class GCMarker;
friend class ExternalTypedData;
friend class ForwardList;
@@ -566,6 +568,7 @@
friend class TypedData;
friend class TypedDataView;
friend class WeakProperty; // StorePointer
+ friend class Instance; // StorePointer
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(RawObject);
@@ -685,6 +688,8 @@
RawObject** to() {
return reinterpret_cast<RawObject**>(&ptr()->source_class_);
}
+
+ friend class Function;
};
@@ -772,6 +777,8 @@
RawObject** to() {
return reinterpret_cast<RawObject**>(&ptr()->closure_);
}
+
+ friend class Function;
};
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 9420790..a3544ad 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -237,6 +237,46 @@
}
+RawObject* SnapshotReader::ReadStaticImplicitClosure(intptr_t object_id,
+ intptr_t class_header) {
+ ASSERT(kind_ == Snapshot::kMessage);
+
+ // First create a function object and associate it with the specified
+ // 'object_id'.
+ Function& func = Function::ZoneHandle(isolate(), Function::null());
+ AddBackRef(object_id, &func, kIsDeserialized);
+
+ // Read the library/class/function information and lookup the function.
+ str_ ^= ReadObjectImpl();
+ library_ = Library::LookupLibrary(str_);
+ if (library_.IsNull() || !library_.Loaded()) {
+ SetReadException("Invalid Library object found in message.");
+ }
+ str_ ^= ReadObjectImpl();
+ if (str_.Equals(Symbols::TopLevel())) {
+ str_ ^= ReadObjectImpl();
+ func = library_.LookupFunctionAllowPrivate(str_);
+ } else {
+ cls_ = library_.LookupClassAllowPrivate(str_);
+ if (cls_.IsNull()) {
+ OS::Print("Name of class not found %s\n", str_.ToCString());
+ SetReadException("Invalid Class object found in message.");
+ }
+ cls_.EnsureIsFinalized(isolate());
+ str_ ^= ReadObjectImpl();
+ func = cls_.LookupFunctionAllowPrivate(str_);
+ }
+ if (func.IsNull()) {
+ SetReadException("Invalid function object found in message.");
+ }
+ func = func.ImplicitClosureFunction();
+ ASSERT(!func.IsNull());
+
+ // Return the associated implicit static closure.
+ return func.ImplicitStaticClosure();
+}
+
+
RawObject* SnapshotReader::ReadObjectImpl() {
int64_t value = Read<int64_t>();
if ((value & kSmiTagMask) == kSmiTag) {
@@ -312,7 +352,8 @@
// Since we are only reading an object reference, If it is an instance kind
// then we only need to figure out the class of the object and allocate an
// instance of it. The individual fields will be read later.
- if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
+ intptr_t header_id = SerializedHeaderData::decode(class_header);
+ if (header_id == kInstanceObjectId) {
Instance& result = Instance::ZoneHandle(isolate(), Instance::null());
AddBackRef(object_id, &result, kIsNotDeserialized);
@@ -326,6 +367,12 @@
result ^= Object::Allocate(cls_.id(), instance_size, HEAP_SPACE(kind_));
}
return result.raw();
+ } else if (header_id == kStaticImplicitClosureObjectId) {
+ // We skip the tags that have been written as the implicit static
+ // closure is going to be created in this isolate or the canonical
+ // version already created in the isolate will be used.
+ ReadTags();
+ return ReadStaticImplicitClosure(object_id, class_header);
}
ASSERT((class_header & kSmiTagMask) != kSmiTag);
@@ -937,7 +984,8 @@
// Read the class header information and lookup the class.
intptr_t class_header = Read<int32_t>();
intptr_t tags = ReadTags();
- if (SerializedHeaderData::decode(class_header) == kInstanceObjectId) {
+ intptr_t header_id = SerializedHeaderData::decode(class_header);
+ if (header_id == kInstanceObjectId) {
// Object is regular dart instance.
Instance* result = reinterpret_cast<Instance*>(GetBackRef(object_id));
intptr_t instance_size = 0;
@@ -1002,6 +1050,11 @@
ASSERT(!result->IsNull());
}
return result->raw();
+ } else if (header_id == kStaticImplicitClosureObjectId) {
+ // We do not use the tags as the implicit static closure
+ // is going to be created in this isolate or the canonical
+ // version already created in the isolate will be used.
+ return ReadStaticImplicitClosure(object_id, class_header);
}
ASSERT((class_header & kSmiTagMask) != kSmiTag);
intptr_t class_id = LookupInternalClass(class_header);
@@ -1215,10 +1268,10 @@
WriteInstanceRef(raw, cls);
return;
}
- // Object is being referenced, add it to the forward ref list and mark
- // it so that future references to this object in the snapshot will use
- // this object id. Mark it as not having been serialized yet so that we
- // will serialize the object when we go through the forward list.
+ // Add object to the forward ref list and mark it so that future references
+ // to this object in the snapshot will use this object id. Mark it as having
+ // been serialized so that we do not serialize the object when we go through
+ // the forward list.
forward_list_.MarkAndAddObject(raw, kIsSerialized);
switch (class_id) {
#define SNAPSHOT_WRITE(clazz) \
@@ -1592,6 +1645,29 @@
}
+void SnapshotWriter::WriteStaticImplicitClosure(intptr_t object_id,
+ RawFunction* func,
+ intptr_t tags) {
+ // Write out the serialization header value for this object.
+ WriteInlinedObjectHeader(object_id);
+
+ // Indicate this is a static implicit closure object.
+ Write<int32_t>(SerializedHeaderData::encode(kStaticImplicitClosureObjectId));
+
+ // Write out the tags.
+ WriteTags(tags);
+
+ // Write out the library url, class name and signature function name.
+ RawClass* cls = GetFunctionOwner(func);
+ ASSERT(cls != Class::null());
+ RawLibrary* library = cls->ptr()->library_;
+ ASSERT(library != Library::null());
+ WriteObjectImpl(library->ptr()->url_);
+ WriteObjectImpl(cls->ptr()->name_);
+ WriteObjectImpl(func->ptr()->name_);
+}
+
+
void SnapshotWriter::ArrayWriteTo(intptr_t object_id,
intptr_t array_kind,
intptr_t tags,
@@ -1625,34 +1701,63 @@
}
-void SnapshotWriter::CheckIfSerializable(RawClass* cls) {
+RawFunction* SnapshotWriter::IsSerializableClosure(RawClass* cls,
+ RawObject* obj) {
if (Class::IsSignatureClass(cls)) {
- // We do not allow closure objects in an isolate message.
- Isolate* isolate = Isolate::Current();
- HANDLESCOPE(isolate);
+ // 'obj' is a closure as its class is a signature class, extract
+ // the function object to check if this closure can be sent in an
+ // isolate message.
+ RawFunction* func = Closure::GetFunction(obj);
+ // We only allow closure of top level methods or static functions in a
+ // class to be sent in isolate messages.
+ if (can_send_any_object() &&
+ Function::IsImplicitStaticClosureFunction(func)) {
+ return func;
+ }
+ // Not a closure of a top level method or static function, throw an
+ // exception as we do not allow these objects to be serialized.
+ HANDLESCOPE(isolate());
+
+ const Class& clazz = Class::Handle(isolate(), cls);
+ const Function& errorFunc = Function::Handle(isolate(), func);
+ ASSERT(!errorFunc.IsNull());
+
+ // All other closures are errors.
const char* format = "Illegal argument in isolate message"
- " : (object is a closure - %s %s)";
+ " : (object is a closure - %s %s)";
UnmarkAll(); // Unmark objects now as we are about to print stuff.
- const Class& clazz = Class::Handle(isolate, cls);
- const Function& func = Function::Handle(isolate,
- clazz.signature_function());
- ASSERT(!func.IsNull());
intptr_t len = OS::SNPrint(NULL, 0, format,
- clazz.ToCString(), func.ToCString()) + 1;
- char* chars = isolate->current_zone()->Alloc<char>(len);
- OS::SNPrint(chars, len, format, clazz.ToCString(), func.ToCString());
+ clazz.ToCString(), errorFunc.ToCString()) + 1;
+ char* chars = isolate()->current_zone()->Alloc<char>(len);
+ OS::SNPrint(chars, len, format, clazz.ToCString(), errorFunc.ToCString());
SetWriteException(Exceptions::kArgument, chars);
}
+ return Function::null();
+}
+
+
+RawClass* SnapshotWriter::GetFunctionOwner(RawFunction* func) {
+ RawObject* owner = func->ptr()->owner_;
+ uword tags = GetObjectTags(owner);
+ intptr_t class_id = RawObject::ClassIdTag::decode(tags);
+ if (class_id == kClassCid) {
+ return reinterpret_cast<RawClass*>(owner);
+ }
+ ASSERT(class_id == kPatchClassCid);
+ return reinterpret_cast<RawPatchClass*>(owner)->ptr()->patched_class_;
+}
+
+
+void SnapshotWriter::CheckForNativeFields(RawClass* cls) {
if (cls->ptr()->num_native_fields_ != 0) {
// We do not allow objects with native fields in an isolate message.
- Isolate* isolate = Isolate::Current();
- HANDLESCOPE(Isolate::Current());
+ HANDLESCOPE(isolate());
const char* format = "Illegal argument in isolate message"
" : (object extends NativeWrapper - %s)";
UnmarkAll(); // Unmark objects now as we are about to print stuff.
- const Class& clazz = Class::Handle(isolate, cls);
+ const Class& clazz = Class::Handle(isolate(), cls);
intptr_t len = OS::SNPrint(NULL, 0, format, clazz.ToCString()) + 1;
- char* chars = isolate->current_zone()->Alloc<char>(len);
+ char* chars = isolate()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, clazz.ToCString());
SetWriteException(Exceptions::kArgument, chars);
}
@@ -1673,11 +1778,20 @@
RawObject* raw,
RawClass* cls,
intptr_t tags) {
- // First check if object is a closure or has native fields.
- CheckIfSerializable(cls);
+ // Check if the instance has native fields and throw an exception if it does.
+ CheckForNativeFields(cls);
+
+ // Check if object is a closure that is serializable, if the object is a
+ // closure that is not serializable this will throw an exception.
+ RawFunction* func = IsSerializableClosure(cls, raw);
+ if (func != Function::null()) {
+ WriteStaticImplicitClosure(object_id, func, tags);
+ return;
+ }
// Object is regular dart instance.
- intptr_t next_field_offset =
+ intptr_t next_field_offset = Class::IsSignatureClass(cls) ?
+ Closure::InstanceSize() :
cls->ptr()->next_field_offset_in_words_ << kWordSizeLog2;
ASSERT(next_field_offset > 0);
@@ -1713,8 +1827,25 @@
void SnapshotWriter::WriteInstanceRef(RawObject* raw, RawClass* cls) {
- // First check if object is a closure or has native fields.
- CheckIfSerializable(cls);
+ // Check if the instance has native fields and throw an exception if it does.
+ CheckForNativeFields(cls);
+
+ // Check if object is a closure that is serializable, if the object is a
+ // closure that is not serializable this will throw an exception.
+ RawFunction* func = IsSerializableClosure(cls, raw);
+ if (func != Function::null()) {
+ // Add object to the forward ref list and mark it so that future references
+ // to this object in the snapshot will use this object id. Mark it as having
+ // been serialized so that we do not serialize the object when we go through
+ // the forward list.
+ forward_list_.MarkAndAddObject(raw, kIsSerialized);
+ uword tags = raw->ptr()->tags_;
+ ASSERT(SerializedHeaderTag::decode(tags) == kObjectId);
+ intptr_t object_id = SerializedHeaderData::decode(tags);
+ tags = forward_list_.NodeForObjectId(object_id)->tags();
+ WriteStaticImplicitClosure(object_id, func, tags);
+ return;
+ }
// Object is being referenced, add it to the forward ref list and mark
// it so that future references to this object in the snapshot will use
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 9440e1a..a30b64e 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -347,6 +347,7 @@
RawObject* AllocateUninitialized(intptr_t class_id, intptr_t size);
RawClass* ReadClassId(intptr_t object_id);
+ RawObject* ReadStaticImplicitClosure(intptr_t object_id, intptr_t cls_header);
RawObject* ReadObjectImpl();
RawObject* ReadObjectImpl(intptr_t header);
RawObject* ReadObjectRef();
@@ -611,6 +612,9 @@
void WriteObjectRef(RawObject* raw);
void WriteClassId(RawClass* cls);
+ void WriteStaticImplicitClosure(intptr_t object_id,
+ RawFunction* func,
+ intptr_t tags);
void WriteObjectImpl(RawObject* raw);
void WriteInlinedObject(RawObject* raw);
void WriteForwardedObjects();
@@ -620,7 +624,9 @@
RawSmi* length,
RawTypeArguments* type_arguments,
RawObject* data[]);
- void CheckIfSerializable(RawClass* cls);
+ RawFunction* IsSerializableClosure(RawClass* cls, RawObject* obj);
+ RawClass* GetFunctionOwner(RawFunction* func);
+ void CheckForNativeFields(RawClass* cls);
void SetWriteException(Exceptions::ExceptionType type, const char* msg);
void WriteInstance(intptr_t object_id,
RawObject* raw,
diff --git a/runtime/vm/snapshot_ids.h b/runtime/vm/snapshot_ids.h
index a7282f7..dc1500e 100644
--- a/runtime/vm/snapshot_ids.h
+++ b/runtime/vm/snapshot_ids.h
@@ -22,7 +22,7 @@
// Object id has been optimized away; reader should use next available id.
kOmittedObjectId,
- kClassIdsOffset = kDoubleObject,
+ kClassIdsOffset = kOmittedObjectId,
// The class ids of predefined classes are included in this list
// at an offset of kClassIdsOffset.
@@ -42,6 +42,7 @@
kArrayType,
kInstanceObjectId,
+ kStaticImplicitClosureObjectId,
kMaxPredefinedObjectIds,
kInvalidIndex = -1,
};
diff --git a/sdk/lib/_internal/compiler/js_lib/js_array.dart b/sdk/lib/_internal/compiler/js_lib/js_array.dart
index a170af2..981cbd2 100644
--- a/sdk/lib/_internal/compiler/js_lib/js_array.dart
+++ b/sdk/lib/_internal/compiler/js_lib/js_array.dart
@@ -554,7 +554,7 @@
Set<E> toSet() => new Set<E>.from(this);
- Iterator<E> get iterator => new ListIterator<E>(this);
+ Iterator<E> get iterator => new ArrayIterator<E>(this);
int get hashCode => Primitives.objectHashCode(this);
@@ -589,7 +589,47 @@
* Dummy subclasses that allow the backend to track more precise
* information about arrays through their type. The CPA type inference
* relies on the fact that these classes do not override [] nor []=.
+ *
+ * These classes are really a fiction, and can have no methods, since
+ * getInterceptor always returns JSArray. We should consider pushing the
+ * 'isGrowable' and 'isMutable' checks into the getInterceptor implementation so
+ * these classes can have specialized implementations. Doing so will challenge
+ * many assuptions in the JS backend.
*/
class JSMutableArray<E> extends JSArray<E> implements JSMutableIndexable {}
class JSFixedArray<E> extends JSMutableArray<E> {}
class JSExtendableArray<E> extends JSMutableArray<E> {}
+
+
+/// An [Iterator] that iterates a JSArray.
+///
+class ArrayIterator<E> implements Iterator<E> {
+ final JSArray<E> _iterable;
+ final int _length;
+ int _index;
+ E _current;
+
+ ArrayIterator(JSArray<E> iterable)
+ : _iterable = iterable, _length = iterable.length, _index = 0;
+
+ E get current => _current;
+
+ bool moveNext() {
+ int length = _iterable.length;
+
+ // We have to do the length check even on fixed length Arrays. If we can
+ // inline moveNext() we might be able to GVN the length and eliminate this
+ // check on known fixed length JSArray.
+ if (_length != length) {
+ throw new ConcurrentModificationError(_iterable);
+ }
+
+ if (_index >= length) {
+ _current = null;
+ return false;
+ }
+ _current = _iterable[_index];
+ _index++;
+ return true;
+ }
+}
diff --git a/tests/compiler/dart2js/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await_js_transform_test.dart
index ab94a56..c301f6a 100644
--- a/tests/compiler/dart2js/async_await_js_transform_test.dart
+++ b/tests/compiler/dart2js/async_await_js_transform_test.dart
@@ -82,7 +82,7 @@
return 4;
}""", """
function(b) {
- var __goto = 0, __completer = new Completer(), __returnValue, __handler = 2, __currentError, __next, __helper;
+ var __goto = 0, __completer = new Completer(), __returnValue, __handler = 2, __currentError, __next = [], __helper;
function __body(__errorCode, __result) {
if (__errorCode === 1) {
__currentError = __result;
@@ -118,7 +118,7 @@
case 13:
// returning from await.
__helper = __result;
- __next = [12];
+ __next.push(12);
// goto finally
__goto = 11;
break;
@@ -149,7 +149,7 @@
// after while
case 6:
// break __outer
- __next = [5];
+ __next.push(5);
// goto finally
__goto = 4;
break;
@@ -708,7 +708,7 @@
}
""", """
function(c, i) {
- var __goto = 0, __completer = new Completer(), __handler = 1, __currentError, __next, x, y, __error, __error1;
+ var __goto = 0, __completer = new Completer(), __handler = 1, __currentError, __next = [], x, y, __error, __error1;
function __body(__errorCode, __result) {
if (__errorCode === 1) {
__currentError = __result;
@@ -763,7 +763,7 @@
case 15:
// join
x = __result;
- __next = [13];
+ __next.push(13);
// goto finally
__goto = 12;
break;
@@ -772,7 +772,7 @@
__handler = 10;
__error1 = __currentError;
y.x = foo(__error1);
- __next = [13];
+ __next.push(13);
// goto finally
__goto = 12;
break;
diff --git a/tests/isolate/function_send1_test.dart b/tests/isolate/function_send1_test.dart
new file mode 100644
index 0000000..073ed76
--- /dev/null
+++ b/tests/isolate/function_send1_test.dart
@@ -0,0 +1,118 @@
+// Copyright (c) 2015, 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.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+void toplevel(port, message) { port.send("toplevel:$message"); }
+Function createFuncToplevel() => (p, m) { p.send(m); };
+class C {
+ Function initializer;
+ Function body;
+ C() : initializer = ((p, m) { throw "initializer"; }) {
+ body = (p, m) { throw "body"; };
+ }
+ static void staticFunc(port, message) { port.send("static:$message"); }
+ static Function createFuncStatic() => (p, m) { throw "static expr"; };
+ void instanceMethod(p, m) { throw "instanceMethod"; }
+ Function createFuncMember() => (p, m) { throw "instance expr"; };
+ void call(n, p) { throw "C"; }
+}
+
+class Callable {
+ void call(p, m) { p.send(["callable", m]); }
+}
+
+
+void main() {
+ asyncStart();
+
+ // top-level functions, static functions, closures, instance methods
+ // or function expressions are not sendable to an isolate spawned using
+ // spawnUri.
+ testUnsendable("toplevel", toplevel);
+ testUnsendable("static", C.staticFunc);
+ var c = new C();
+ testUnsendable("instance method", c.instanceMethod);
+ testUnsendable("static context expression", createFuncToplevel());
+ testUnsendable("static context expression", C.createFuncStatic());
+ testUnsendable("initializer context expression", c.initializer);
+ testUnsendable("constructor context expression", c.body);
+ testUnsendable("instance method context expression", c.createFuncMember());
+ testUnsendable("toplevel", toplevel.call);
+ testUnsendable("static", C.staticFunc.call);
+ testUnsendable("callable object", new Callable().call);
+
+ asyncEnd();
+ return;
+}
+
+// Create a receive port that expects exactly one message.
+// Pass the message to `callback` and return the sendPort.
+SendPort singleMessagePort(callback) {
+ var p;
+ p = new RawReceivePort((v) { p.close(); callback(v); });
+ return p.sendPort;
+}
+
+// A singleMessagePort that expects the message to be a specific value.
+SendPort expectMessagePort(message) {
+ asyncStart();
+ return singleMessagePort((v) {
+ Expect.equals(message, v);
+ asyncEnd();
+ });
+}
+
+// Creates a new isolate and a pair of ports that expect a single message
+// to be sent to the other isolate and back to the callback function.
+Future<SendPort> echoPort(callback(value)) {
+ Completer completer = new Completer<SendPort>();
+ SendPort replyPort = singleMessagePort(callback);
+ RawReceivePort initPort;
+ initPort = new RawReceivePort((p) {
+ completer.complete(p);
+ initPort.close();
+ });
+ return Isolate.spawn(_echo, [replyPort, initPort.sendPort])
+ .then((isolate) => completer.future);
+}
+
+void _echo(msg) {
+ var replyPort = msg[0];
+ RawReceivePort requestPort;
+ requestPort = new RawReceivePort((msg) {
+ replyPort.send(msg);
+ requestPort.close(); // Single echo only.
+ });
+ msg[1].send(requestPort.sendPort);
+}
+
+// Creates other isolate that waits for a single message, `msg`, on the returned
+// port, and executes it as `msg[0](msg[1],msg[2])` in the other isolate.
+Future<SendPort> callPort() {
+ Completer completer = new Completer<SendPort>();
+ SendPort initPort = singleMessagePort(completer.complete);
+ return Isolate.spawn(_call, initPort)
+ .then((_) => completer.future);
+}
+
+void _call(initPort) {
+ initPort.send(singleMessagePort(callFunc));
+}
+
+void testUnsendable(name, func) {
+ asyncStart();
+ Isolate.spawnUri(Uri.parse("function_send_test.dart"), [], func)
+ .then((v) => throw "allowed spawn direct?", onError: (e,s){ asyncEnd(); });
+ asyncStart();
+ Isolate.spawnUri(Uri.parse("function_send_test.dart"), [], [func])
+ .then((v) => throw "allowed spawn wrapped?", onError: (e,s){ asyncEnd(); });
+}
+
+void callFunc(message) {
+ message[0](message[1], message[2]);
+}
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index 8f9e66f..30a218e 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -21,11 +21,9 @@
handle_error_test: Fail # Not implemented yet
handle_error2_test: Fail # Not implemented yet
handle_error3_test: Fail # Not implemented yet
-function_send_test: Fail # Not implemented yet
message3_test/constList_identical: RuntimeError # Issue 21816
message3_test/constMap: RuntimeError # Issue 21816
-message3_test/fun: RuntimeError # Issue 21585
message3_test/constInstance: RuntimeError # Issue 21816
message3_test/byteBuffer: Crash # Issue 21818
message3_test/int32x4: Crash # Issue 21818
@@ -53,6 +51,7 @@
issue_21398_parent_isolate_test: SkipByDesign # Test uses a ".dart" URI.
issue_21398_parent_isolate1_test: SkipByDesign # Test uses a ".dart" URI.
issue_21398_parent_isolate2_test: SkipByDesign # Test uses a ".dart" URI.
+function_send1_test: SkipByDesign # Test uses a ".dart" URI.
message3_test/constList: RuntimeError # Issue 21817
message3_test/constList_identical: RuntimeError # Issue 21817
message3_test/constMap: RuntimeError # Issue 21817
@@ -100,6 +99,7 @@
spawn_uri_nested_vm_test: Skip # Issue 14479: This test is timing out.
[ $compiler == none && ( $runtime == dartium || $runtime == drt || $runtime == ContentShellOnAndroid) ]
+message_enum_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
compile_time_error_test/none: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
isolate_import_test/none: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
issue_21398_parent_isolate_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
@@ -112,6 +112,7 @@
spawn_uri_missing_from_isolate_test: RuntimeError # Issue 17649
spawn_uri_missing_test: SkipSlow # Times out.
isolate_current_test: Fail, OK # Issue 13921 Dom isolates don't support spawnFunction
+function_send_test: Fail, OK # 13921 Dom isolates don't support spawnFunction
[ $compiler == dartanalyzer || $compiler == dart2analyzer ]
browser/typed_data_message_test: StaticWarning
diff --git a/tests/language/async_throw_in_catch_test.dart b/tests/language/async_throw_in_catch_test.dart
index 1cb741e..b3ecbab 100644
--- a/tests/language/async_throw_in_catch_test.dart
+++ b/tests/language/async_throw_in_catch_test.dart
@@ -404,6 +404,40 @@
tracer.trace("h");
}
+foo17(Tracer tracer) async {
+ try {
+ tracer.trace("a");
+ } finally {
+ try {
+ tracer.trace("b");
+ throw "Error";
+ } catch (error) {
+ await new Future.value(3); /// forceAwait: continued
+ Expect.equals("Error", error);
+ tracer.trace("c");
+ } finally {
+ tracer.trace("d");
+ }
+ tracer.trace("e");
+ }
+ tracer.trace("f");
+}
+
+foo18(Tracer tracer) async {
+ try {
+ tracer.trace("a");
+ } finally {
+ try {
+ tracer.trace("b");
+ } finally {
+ await new Future.value(3); /// forceAwait: continued
+ tracer.trace("c");
+ }
+ tracer.trace("d");
+ }
+ tracer.trace("e");
+}
+
runTest(expectedTrace, fun, [expectedError]) async {
Tracer tracer = new Tracer(expectedTrace);
try {
@@ -433,6 +467,8 @@
await runTest("abcdefgX", foo14, "Error3");
await runTest("abcdefg", foo15);
await runTest("abcdfg", foo16);
+ await runTest("abcdef", foo17);
+ await runTest("abcde", foo18);
}
void main() {
diff --git a/tools/VERSION b/tools/VERSION
index 8c63f7d..9f2b03f 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 9
PATCH 0
PRERELEASE 10
-PRERELEASE_PATCH 3
+PRERELEASE_PATCH 4