Version 1.12.0-dev.5.3
Cherry-pick f3f52d4b59081e2f294126ac7f69f902968692d7 to dev
Cherry-pick b30a7a1750cd13c92781480f8708974b84385cad to dev
Cherry-pick 73cfbce0bca51c92e589fc7d85869478f044dc11 to dev
Cherry-pick b6ffeafbee0eb22de30c88d2cb1b9bcf3bfe9723 to dev
Cherry-pick a8e2180b781ad82d81206696da220a920310b5db to dev
Cherry-pick fc55f38aaf053a62d1959b09644ecff49fc35d43 to dev
Cherry-pick 56c91201c32b8213d37849e22dbabf392eef1bd6 to dev
Cherry-pick c06e66c9b92cc6a5faf6179c43fdb3a0bec6e34c to dev
Cherry-pick 532474ab358eea3b85312465f4fa60626a0b820b to dev
Cherry-pick cc981c8246e28e21fe84b8ec00b7a2eac9719102 to dev
Cherry-pick 480cd7fdc82dc861745acda57ebd9341aaf1bd80 to dev
diff --git a/DEPS b/DEPS
index 56677bd..3b2587b 100644
--- a/DEPS
+++ b/DEPS
@@ -38,7 +38,7 @@
# Revisions of /third_party/* dependencies.
"7zip_rev" : "@19997",
- "analyzer_cli_rev" : "@c4e628a8467f75ded563f029dfb2f1738856f471",
+ "analyzer_cli_rev" : "@4281dbf08ccc20b20b795b438766b57afb16ad13",
"args_tag": "@0.13.0",
"async_tag": "@1.2.0",
"barback_tag" : "@0.15.2+6",
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 3c8f1a7..d1ec86e 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -1438,7 +1438,7 @@
String libraryFile = librarySource.fullName;
// may be "package:" URI
{
- String libraryPackageUri = findAbsoluteUri(context, libraryFile);
+ String libraryPackageUri = findNonFileUri(context, libraryFile);
if (libraryPackageUri != null) {
_addFix_importLibrary(
DartFixKind.IMPORT_LIBRARY_PROJECT, libraryPackageUri);
@@ -1630,7 +1630,7 @@
if (substringAfterLast(libFile, '/') == uriName) {
String fixedUri;
// may be "package:" URI
- String libPackageUri = findAbsoluteUri(context, libFile);
+ String libPackageUri = findNonFileUri(context, libFile);
if (libPackageUri != null) {
fixedUri = libPackageUri;
} else {
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index c4082fa..1af70b0 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -104,10 +104,11 @@
*
* Returns the absolute (non-file) URI or `null`.
*/
-String findAbsoluteUri(AnalysisContext context, String path) {
- Source fileSource = new NonExistingSource(path, null, UriKind.FILE_URI);
+String findNonFileUri(AnalysisContext context, String path) {
+ Source fileSource =
+ new NonExistingSource(path, toUri(path), UriKind.FILE_URI);
Uri uri = context.sourceFactory.restoreUri(fileSource);
- if (uri == null) {
+ if (uri == null || uri.scheme == 'file') {
return null;
}
return uri.toString();
diff --git a/pkg/analysis_server/test/services/correction/fix_test.dart b/pkg/analysis_server/test/services/correction/fix_test.dart
index f0692c6..13d3d99 100644
--- a/pkg/analysis_server/test/services/correction/fix_test.dart
+++ b/pkg/analysis_server/test/services/correction/fix_test.dart
@@ -4405,7 +4405,7 @@
'my_pkg': [myPkgFolder]
});
context.sourceFactory = new SourceFactory(
- [AbstractContextTest.SDK_RESOLVER, resourceResolver, pkgResolver]);
+ [AbstractContextTest.SDK_RESOLVER, pkgResolver, resourceResolver]);
// force 'my_pkg' resolution
addSource('/tmp/other.dart', "import 'package:my_pkg/my_lib.dart';");
}
diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart
index 11db8fe..21a5ab0 100644
--- a/runtime/lib/string_patch.dart
+++ b/runtime/lib/string_patch.dart
@@ -896,14 +896,16 @@
List<String> result = new List<String>();
int startIndex = 0;
int previousIndex = 0;
+ // 'pattern' may not be implemented correctly and therefore we cannot
+ // call _substringUnhchecked unless it is a trustworthy type (e.g. String).
while (true) {
if (startIndex == length || !iterator.moveNext()) {
- result.add(this._substringUnchecked(previousIndex, length));
+ result.add(this.substring(previousIndex, length));
break;
}
Match match = iterator.current;
if (match.start == length) {
- result.add(this._substringUnchecked(previousIndex, length));
+ result.add(this.substring(previousIndex, length));
break;
}
int endIndex = match.end;
@@ -911,7 +913,7 @@
++startIndex; // empty match, advance and restart
continue;
}
- result.add(this._substringUnchecked(previousIndex, match.start));
+ result.add(this.substring(previousIndex, match.start));
startIndex = previousIndex = endIndex;
}
return result;
diff --git a/runtime/observatory/lib/src/elements/class_view.html b/runtime/observatory/lib/src/elements/class_view.html
index 200c22a..a8f8165 100644
--- a/runtime/observatory/lib/src/elements/class_view.html
+++ b/runtime/observatory/lib/src/elements/class_view.html
@@ -240,9 +240,8 @@
</div>
</div>
- <hr>
-
<div class="content-centered-big">
+ <hr>
<source-inset location="{{ cls.location }}">
</source-inset>
</div>
diff --git a/runtime/observatory/lib/src/elements/eval_box.dart b/runtime/observatory/lib/src/elements/eval_box.dart
index 7447aca..c6fd2c5 100644
--- a/runtime/observatory/lib/src/elements/eval_box.dart
+++ b/runtime/observatory/lib/src/elements/eval_box.dart
@@ -24,7 +24,7 @@
void updateLineMode(Event e, var detail, Node target) {
lineMode = (e.target as InputElement).value;
- if (lineMode == '1-line') {
+ if (lineMode == '1-line' && text != null) {
text = text.replaceAll('\n', ' ');
}
}
diff --git a/runtime/observatory/lib/src/elements/eval_box.html b/runtime/observatory/lib/src/elements/eval_box.html
index ad9045a..f01a995 100644
--- a/runtime/observatory/lib/src/elements/eval_box.html
+++ b/runtime/observatory/lib/src/elements/eval_box.html
@@ -11,10 +11,13 @@
font: 400 16px 'Montserrat', sans-serif;
}
.bigtextbox {
+ flex-grow: 1;
font: 400 16px 'Montserrat', sans-serif;
}
.button {
font: 400 16px 'Montserrat', sans-serif;
+ margin-left: 0.5em;
+ margin-right: 0.5em;
}
.radios {
display: inline;
@@ -64,7 +67,7 @@
<input class="textbox" type="text" value="{{ text }}">
</template>
<template if="{{ lineMode == 'N-line' }}">
- <textarea class="bigtextbox" rows="5" cols="80"
+ <textarea class="bigtextbox"
value="{{ text }}"></textarea>
</template>
diff --git a/runtime/observatory/lib/src/elements/field_view.html b/runtime/observatory/lib/src/elements/field_view.html
index 3e16d1b..d32cab0 100644
--- a/runtime/observatory/lib/src/elements/field_view.html
+++ b/runtime/observatory/lib/src/elements/field_view.html
@@ -24,6 +24,7 @@
<div class="content">
<h1>
+ field
<template if="{{ field.isStatic }}">static</template>
<template if="{{ field.isFinal }}">final</template>
<template if="{{ field.isConst }}">const</template>
@@ -83,6 +84,15 @@
</template>
</div>
</div>
+
+ <div class="content-centered-big">
+ <hr>
+ <script-inset script="{{ field.location.script }}"
+ startPos="{{ field.location.tokenPos }}"
+ endPos="{{ field.location.tokenPos }}">
+ </script-inset>
+ </div>
+
<view-footer></view-footer>
</template>
</polymer-element>
diff --git a/runtime/observatory/lib/src/elements/isolate_view.dart b/runtime/observatory/lib/src/elements/isolate_view.dart
index fc6116b..de8487c 100644
--- a/runtime/observatory/lib/src/elements/isolate_view.dart
+++ b/runtime/observatory/lib/src/elements/isolate_view.dart
@@ -79,6 +79,7 @@
@CustomTag('isolate-view')
class IsolateViewElement extends ObservatoryElement {
@published Isolate isolate;
+ @published Library rootLibrary;
Timer _updateTimer;
TagProfileChart tagProfileChart = new TagProfileChart();
IsolateViewElement.created() : super.created();
@@ -106,6 +107,7 @@
if (isolate.topFrame != null) {
isolate.topFrame.function.load();
}
+ isolate.rootLibrary.load().then((lib) => rootLibrary = lib);
}
@override
diff --git a/runtime/observatory/lib/src/elements/isolate_view.html b/runtime/observatory/lib/src/elements/isolate_view.html
index 014c191..46b49ee 100644
--- a/runtime/observatory/lib/src/elements/isolate_view.html
+++ b/runtime/observatory/lib/src/elements/isolate_view.html
@@ -140,6 +140,13 @@
<div class="content">
<eval-box callback="{{ evaluate }}"></eval-box>
</div>
+
+ <div class="content-centered-big">
+ <hr>
+ <script-inset script="{{ rootLibrary.rootScript }}">
+ </script-inset>
+ </div>
+
<view-footer></view-footer>
</template>
</polymer-element>
diff --git a/runtime/observatory/lib/src/elements/library_view.html b/runtime/observatory/lib/src/elements/library_view.html
index a898ebb..408916d 100644
--- a/runtime/observatory/lib/src/elements/library_view.html
+++ b/runtime/observatory/lib/src/elements/library_view.html
@@ -149,6 +149,12 @@
</template>
</div>
+ <div class="content-centered-big">
+ <hr>
+ <script-inset script="{{ library.rootScript }}">
+ </script-inset>
+ </div>
+
<view-footer></view-footer>
</template>
</polymer-element>
diff --git a/runtime/observatory/lib/src/elements/ports.html b/runtime/observatory/lib/src/elements/ports.html
index 70aac0b..d5b3152 100644
--- a/runtime/observatory/lib/src/elements/ports.html
+++ b/runtime/observatory/lib/src/elements/ports.html
@@ -24,7 +24,7 @@
<nav-refresh callback="{{ refresh }}"></nav-refresh>
</nav-bar>
<div class="content">
- <h1>Ports</h1>
+ <h1>Ports ({{ports.length}})</h1>
<hr>
<div class="memberList">
<template repeat="{{ port in ports }}">
diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart
index 66b125c..3aeaf67 100644
--- a/runtime/observatory/lib/src/elements/script_inset.dart
+++ b/runtime/observatory/lib/src/elements/script_inset.dart
@@ -11,6 +11,7 @@
import 'package:observatory/service.dart';
import 'package:observatory/utils.dart';
import 'package:polymer/polymer.dart';
+import 'package:logging/logging.dart';
const nbsp = "\u00A0";
@@ -98,6 +99,52 @@
}
}
+class LibraryAnnotation extends Annotation {
+ Library target;
+ LibraryAnnotation(this.target);
+
+ void applyStyleTo(element) {
+ if (element == null) {
+ return; // TODO(rmacnak): Handling overlapping annotations.
+ }
+ element.style.fontWeight = "bold";
+ element.title = "library ${target.uri}";
+
+ addInfoBox(element, () {
+ var details = table();
+ var r = row();
+ r.append(cell("Library"));
+ r.append(cell(serviceRef(target)));
+ details.append(r);
+
+ return details;
+ });
+ }
+}
+
+class PartAnnotation extends Annotation {
+ Script part;
+ PartAnnotation(this.part);
+
+ void applyStyleTo(element) {
+ if (element == null) {
+ return; // TODO(rmacnak): Handling overlapping annotations.
+ }
+ element.style.fontWeight = "bold";
+ element.title = "script ${part.uri}";
+
+ addInfoBox(element, () {
+ var details = table();
+ var r = row();
+ r.append(cell("Script"));
+ r.append(cell(serviceRef(part)));
+ details.append(r);
+
+ return details;
+ });
+ }
+}
+
class LocalVariableAnnotation extends Annotation {
final value;
@@ -169,6 +216,7 @@
columnStop = 0;
return;
}
+
Script script = location.script;
line = script.tokenToLine(location.tokenPos);
columnStart = script.tokenToCol(location.tokenPos);
@@ -455,9 +503,9 @@
if (!inDebuggerContext && script.library != null) {
loadDeclarationsOfLibrary(script.library);
-
- // Add fields before functions so they beat out conflicting
- // implicit g/setters.
+ addLibraryAnnotations();
+ addDependencyAnnotations();
+ addPartAnnotations();
addClassAnnotations();
addFieldAnnotations();
addFunctionAnnotations();
@@ -500,6 +548,106 @@
});
}
+ void addLibraryAnnotations() {
+ for (ScriptLine line in script.lines) {
+ // TODO(rmacnak): Use a real scanner.
+ var pattern = new RegExp("library ${script.library.name}");
+ var match = pattern.firstMatch(line.text);
+ if (match != null) {
+ var anno = new LibraryAnnotation(script.library);
+ anno.line = line.line;
+ anno.columnStart = match.start + 8;
+ anno.columnStop = match.end;
+ annotations.add(anno);
+ }
+ // TODO(rmacnak): Use a real scanner.
+ pattern = new RegExp("part of ${script.library.name}");
+ match = pattern.firstMatch(line.text);
+ if (match != null) {
+ var anno = new LibraryAnnotation(script.library);
+ anno.line = line.line;
+ anno.columnStart = match.start + 8;
+ anno.columnStop = match.end;
+ annotations.add(anno);
+ }
+ }
+ }
+
+ Library resolveDependency(String relativeUri) {
+ var targetUri = Uri.parse(script.library.uri).resolve(relativeUri);
+ for (Library l in script.isolate.libraries) {
+ if (targetUri.toString() == l.uri) {
+ return l;
+ }
+ }
+ Logger.root.info("Could not resolve library dependency: $relativeUri");
+ return null;
+ }
+
+ void addDependencyAnnotations() {
+ // TODO(rmacnak): Use a real scanner.
+ var patterns = [
+ new RegExp("import '(.*)'"),
+ new RegExp('import "(.*)"'),
+ new RegExp("export '(.*)'"),
+ new RegExp('export "(.*)"'),
+ ];
+ for (ScriptLine line in script.lines) {
+ for (var pattern in patterns) {
+ var match = pattern.firstMatch(line.text);
+ if (match != null) {
+ Library target = resolveDependency(match[1]);
+ if (target != null) {
+ var anno = new LibraryAnnotation(target);
+ anno.line = line.line;
+ anno.columnStart = match.start + 8;
+ anno.columnStop = match.end - 1;
+ annotations.add(anno);
+ }
+ }
+ }
+ }
+ }
+
+ Script resolvePart(String relativeUri) {
+ var rootUri = Uri.parse(script.library.uri);
+ if (rootUri.scheme == 'dart') {
+ // The relative paths from dart:* libraries to their parts are not valid.
+ rootUri = new Uri.directory(script.library.uri);
+ }
+ var targetUri = rootUri.resolve(relativeUri);
+ for (Script s in script.library.scripts) {
+ if (targetUri.toString() == s.uri) {
+ return s;
+ }
+ }
+ Logger.root.info("Could not resolve part: $relativeUri");
+ return null;
+ }
+
+ void addPartAnnotations() {
+ // TODO(rmacnak): Use a real scanner.
+ var patterns = [
+ new RegExp("part '(.*)'"),
+ new RegExp('part "(.*)"'),
+ ];
+ for (ScriptLine line in script.lines) {
+ for (var pattern in patterns) {
+ var match = pattern.firstMatch(line.text);
+ if (match != null) {
+ Script part = resolvePart(match[1]);
+ if (part != null) {
+ var anno = new PartAnnotation(part);
+ anno.line = line.line;
+ anno.columnStart = match.start + 6;
+ anno.columnStop = match.end - 1;
+ annotations.add(anno);
+ }
+ }
+ }
+ }
+ }
+
void addClassAnnotations() {
for (var cls in script.library.classes) {
if ((cls.location != null) && (cls.location.script == script)) {
@@ -525,13 +673,23 @@
void addFunctionAnnotations() {
for (var func in script.library.functions) {
- if ((func.location != null) && (func.location.script == script)) {
+ if ((func.location != null) &&
+ (func.location.script == script) &&
+ (func.kind != FunctionKind.kImplicitGetterFunction) &&
+ (func.kind != FunctionKind.kImplicitSetterFunction)) {
+ // We annotate a field declaration with the field instead of the
+ // implicit getter or setter.
annotations.add(new FunctionDeclarationAnnotation(func));
}
}
for (var cls in script.library.classes) {
for (var func in cls.functions) {
- if ((func.location != null) && (func.location.script == script)) {
+ if ((func.location != null) &&
+ (func.location.script == script) &&
+ (func.kind != FunctionKind.kImplicitGetterFunction) &&
+ (func.kind != FunctionKind.kImplicitSetterFunction)) {
+ // We annotate a field declaration with the field instead of the
+ // implicit getter or setter.
annotations.add(new FunctionDeclarationAnnotation(func));
}
}
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 1c6fc0f..2e69952 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -1951,6 +1951,13 @@
return isolate._eval(this, expression);
}
+ Script get rootScript {
+ for (Script script in scripts) {
+ if (script.uri == uri) return script;
+ }
+ return null;
+ }
+
String toString() => "Library($uri)";
}
@@ -3149,8 +3156,16 @@
if (address == 0) {
return;
}
-
- jumpTarget = instructionsByAddressOffset[address - startAddress];
+ var relativeAddress = address - startAddress;
+ if (relativeAddress < 0) {
+ Logger.root.warning('Bad address resolving jump target $relativeAddress');
+ return;
+ }
+ if (relativeAddress >= instructionsByAddressOffset.length) {
+ Logger.root.warning('Bad address resolving jump target $relativeAddress');
+ return;
+ }
+ jumpTarget = instructionsByAddressOffset[relativeAddress];
}
}
@@ -3350,7 +3365,7 @@
var pcOffset = 0;
if (disassembly[i] != '') {
// Not a code comment, extract address.
- address = int.parse(disassembly[i]);
+ address = int.parse(disassembly[i], radix:16);
pcOffset = address - startAddress;
}
var instruction = new CodeInstruction(address, pcOffset, machine, human);
diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h
index cc6612e..0d49310 100644
--- a/runtime/platform/globals.h
+++ b/runtime/platform/globals.h
@@ -306,6 +306,13 @@
#define Pu64 PRIu64
#define Px64 PRIx64
+// Zero-padded pointer
+#if defined(ARCH_IS_32_BIT)
+#define Pp "08" PRIxPTR
+#else
+#define Pp "016" PRIxPTR
+#endif
+
// Suffixes for 64-bit integer literals.
#ifdef _MSC_VER
diff --git a/runtime/vm/disassembler.cc b/runtime/vm/disassembler.cc
index 80e1ff6..1a92450 100644
--- a/runtime/vm/disassembler.cc
+++ b/runtime/vm/disassembler.cc
@@ -46,12 +46,11 @@
char* human_buffer,
intptr_t human_size,
uword pc) {
- uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
// Instructions are represented as three consecutive values in a JSON array.
// All three are strings. The first is the address of the instruction,
// the second is the hex string of the code, and the final is a human
// readable string.
- jsarr_.AddValueF("%p", pc_ptr);
+ jsarr_.AddValueF("%" Pp "", pc);
jsarr_.AddValue(hex_buffer);
jsarr_.AddValue(human_buffer);
}
diff --git a/runtime/vm/disassembler_x64.cc b/runtime/vm/disassembler_x64.cc
index dba7e42..9f43a38 100644
--- a/runtime/vm/disassembler_x64.cc
+++ b/runtime/vm/disassembler_x64.cc
@@ -878,7 +878,7 @@
int DisassemblerX64::JumpConditionalShort(uint8_t* data) {
uint8_t cond = *data & 0x0F;
uint8_t b = *(data + 1);
- uint8_t* dest = data + static_cast<uint8_t>(b) + 2;
+ uint8_t* dest = data + static_cast<int8_t>(b) + 2;
const char* mnem = conditional_code_suffix[cond];
AppendToBuffer("j%s ", mnem);
AppendAddressToBuffer(dest);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 4351b1d..38c5c6b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15604,6 +15604,7 @@
return this->raw();
}
ASSERT(this->Equals(type));
+ ASSERT(type.IsCanonical());
return type.raw();
}
@@ -15625,6 +15626,7 @@
}
ASSERT(type.IsFinalized());
if (this->Equals(type)) {
+ ASSERT(type.IsCanonical());
return type.raw();
}
index++;
@@ -15653,6 +15655,7 @@
}
ASSERT(type.IsFinalized());
if (this->Equals(type)) {
+ ASSERT(type.IsCanonical());
return type.raw();
}
index++;
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 419cd4e..fed3ae5 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -10568,7 +10568,8 @@
AstNode* Parser::CreateAssignmentNode(AstNode* original,
AstNode* rhs,
const String* left_ident,
- intptr_t left_pos) {
+ intptr_t left_pos,
+ bool is_compound /* = false */) {
AstNode* result = original->MakeAssignmentNode(rhs);
if (result == NULL) {
String& name = String::ZoneHandle(Z);
@@ -10606,17 +10607,18 @@
// normally: a op= b ==> a = a op b
// however: a ??= b ==> a ?? (a = b)
// Therefore, we need to transform a = (a ?? b) into a ?? (a = b)
- if (rhs->IsBinaryOpNode() &&
+ if (is_compound &&
+ rhs->IsBinaryOpNode() &&
(rhs->AsBinaryOpNode()->kind() == Token::kIFNULL)) {
BinaryOpNode* ifnull = rhs->AsBinaryOpNode();
AstNode* modified_assign =
- CreateAssignmentNode(ifnull->left(),
+ CreateAssignmentNode(original,
ifnull->right(),
left_ident,
left_pos);
result = new(Z) BinaryOpNode(rhs->token_pos(),
Token::kIFNULL,
- original,
+ ifnull->left(),
modified_assign);
}
return result;
@@ -10658,7 +10660,7 @@
right_expr =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr =
- CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos);
+ CreateAssignmentNode(expr, right_expr, expr_ident, expr_pos, true);
ASSERT(assign_expr != NULL);
let_expr->AddNode(assign_expr);
expr = let_expr;
@@ -10771,7 +10773,7 @@
AstNode* assigned_value =
ExpandAssignableOp(assignment_pos, assignment_op, expr, right_expr);
AstNode* assign_expr =
- CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos);
+ CreateAssignmentNode(expr, assigned_value, expr_ident, expr_pos, true);
ASSERT(assign_expr != NULL);
let_expr->AddNode(assign_expr);
return let_expr;
@@ -10872,7 +10874,8 @@
binary_op,
expr,
new(Z) LiteralNode(op_pos, Smi::ZoneHandle(Z, Smi::New(1))));
- AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos);
+ AstNode* store =
+ CreateAssignmentNode(expr, add, expr_ident, expr_pos, true);
ASSERT(store != NULL);
let_expr->AddNode(store);
expr = let_expr;
@@ -11589,7 +11592,8 @@
binary_op,
new(Z) LoadLocalNode(expr_pos, temp),
new(Z) LiteralNode(expr_pos, Smi::ZoneHandle(Z, Smi::New(1))));
- AstNode* store = CreateAssignmentNode(expr, add, expr_ident, expr_pos);
+ AstNode* store =
+ CreateAssignmentNode(expr, add, expr_ident, expr_pos, true);
ASSERT(store != NULL);
// The result is a pair of the (side effects of the) store followed by
// the (value of the) initial value temp variable load.
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 2e74b1c..5199cb6 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -799,7 +799,8 @@
AstNode* CreateAssignmentNode(AstNode* original,
AstNode* rhs,
const String* left_ident,
- intptr_t left_pos);
+ intptr_t left_pos,
+ bool is_compound = false);
AstNode* InsertClosureCallNodes(AstNode* condition);
ConstructorCallNode* CreateConstructorCallNode(
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 9a12d08..0cff97a 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -209,10 +209,14 @@
Snapshot::Kind kind) {
ASSERT(reader != NULL);
+ // Determine if the type class of this type is in the full snapshot.
+ bool typeclass_is_in_fullsnapshot = reader->Read<bool>();
+
// Allocate type object.
Type& type = Type::ZoneHandle(reader->zone(), NEW_OBJECT(Type));
bool is_canonical = RawObject::IsCanonical(tags);
- bool defer_canonicalization = is_canonical && (kind != Snapshot::kFull);
+ bool defer_canonicalization = is_canonical &&
+ (kind != Snapshot::kFull && typeclass_is_in_fullsnapshot);
reader->AddBackRef(object_id, &type, kIsDeserialized, defer_canonicalization);
// Set all non object fields.
@@ -257,6 +261,14 @@
writer->WriteIndexedObject(kTypeCid);
writer->WriteTags(writer->GetObjectTags(this));
+ // Write out typeclass_is_in_fullsnapshot first as this will
+ // help the reader decide on how to canonicalize the type object.
+ intptr_t tags = writer->GetObjectTags(ptr()->type_class_);
+ bool typeclass_is_in_fullsnapshot =
+ (ClassIdTag::decode(tags) == kClassCid) &&
+ Class::IsInFullSnapshot(reinterpret_cast<RawClass*>(ptr()->type_class_));
+ writer->Write<bool>(typeclass_is_in_fullsnapshot);
+
// Write out all the non object pointer fields.
writer->Write<int32_t>(ptr()->token_pos_);
writer->Write<int8_t>(ptr()->type_state_);
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 907e805..4b92acd 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1168,41 +1168,36 @@
BackRefNode& backref = (*backward_references_)[i];
if (backref.defer_canonicalization()) {
Object* objref = backref.reference();
- bool needs_patching = false;
// Object should either be an abstract type or a type argument.
if (objref->IsType()) {
typeobj ^= objref->raw();
newobj = typeobj.Canonicalize();
- if ((newobj.raw() != typeobj.raw()) && !typeobj.IsRecursive()) {
- needs_patching = true;
- } else {
- // Set Canonical bit.
- objref->SetCanonical();
- }
} else {
ASSERT(objref->IsTypeArguments());
typeargs ^= objref->raw();
newobj = typeargs.Canonicalize();
- if ((newobj.raw() != typeargs.raw()) && !typeargs.IsRecursive()) {
- needs_patching = true;
- } else {
- // Set Canonical bit.
- objref->SetCanonical();
- }
}
- if (needs_patching) {
+ if (newobj.raw() != objref->raw()) {
ZoneGrowableArray<intptr_t>* patches = backref.patch_records();
ASSERT(newobj.IsCanonical());
ASSERT(patches != NULL);
+ // First we replace the back ref table with the canonical object.
+ *objref = newobj.raw();
+ // Now we go over all the patch records and patch the canonical object.
for (intptr_t j = 0; j < patches->length(); j+=2) {
NoSafepointScope no_safepoint;
intptr_t patch_object_id = (*patches)[j];
intptr_t patch_offset = (*patches)[j + 1];
Object* target = GetBackRef(patch_object_id);
- RawObject** rawptr =
- reinterpret_cast<RawObject**>(target->raw()->ptr());
- target->StorePointer((rawptr + patch_offset), newobj.raw());
+ // We should not backpatch an object that is canonical.
+ if (!target->IsCanonical()) {
+ RawObject** rawptr =
+ reinterpret_cast<RawObject**>(target->raw()->ptr());
+ target->StorePointer((rawptr + patch_offset), newobj.raw());
+ }
}
+ } else {
+ ASSERT(objref->IsCanonical());
}
}
}
diff --git a/sdk/lib/collection/iterable.dart b/sdk/lib/collection/iterable.dart
index 3290682..e89acc6f 100644
--- a/sdk/lib/collection/iterable.dart
+++ b/sdk/lib/collection/iterable.dart
@@ -103,16 +103,16 @@
bool get isNotEmpty => !isEmpty;
- Iterable<E> take(int n) {
- return new TakeIterable<E>(this, n);
+ Iterable<E> take(int count) {
+ return new TakeIterable<E>(this, count);
}
Iterable<E> takeWhile(bool test(E value)) {
return new TakeWhileIterable<E>(this, test);
}
- Iterable<E> skip(int n) {
- return new SkipIterable<E>(this, n);
+ Iterable<E> skip(int count) {
+ return new SkipIterable<E>(this, count);
}
Iterable<E> skipWhile(bool test(E value)) {
diff --git a/sdk/lib/core/iterable.dart b/sdk/lib/core/iterable.dart
index 3de39bc..78efa1b 100644
--- a/sdk/lib/core/iterable.dart
+++ b/sdk/lib/core/iterable.dart
@@ -392,8 +392,8 @@
*
* The `count` must not be negative.
*/
- Iterable<E> take(int n) {
- return new TakeIterable<E>(this, n);
+ Iterable<E> take(int count) {
+ return new TakeIterable<E>(this, count);
}
/**
@@ -422,8 +422,8 @@
*
* The `count` must not be negative.
*/
- Iterable<E> skip(int n) {
- return new SkipIterable<E>(this, n);
+ Iterable<E> skip(int count) {
+ return new SkipIterable<E>(this, count);
}
/**
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index d6e6134..a539fa1 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -393,12 +393,14 @@
string_interpolate1_negative_test: CompileTimeError
string_interpolate2_negative_test: CompileTimeError
string_interpolate_test: StaticWarning
+string_interpolation_and_buffer_test: StaticWarning
string_interpolation1_negative_test: CompileTimeError
string_interpolation2_negative_test: CompileTimeError
string_interpolation3_negative_test: CompileTimeError
string_interpolation4_negative_test: CompileTimeError
string_interpolation5_negative_test: CompileTimeError
string_interpolation6_negative_test: CompileTimeError
+string_split_test: StaticWarning
string_test: StaticWarning
string_unicode1_negative_test: CompileTimeError
string_unicode2_negative_test: CompileTimeError
diff --git a/tests/language/nullaware_opt_test.dart b/tests/language/nullaware_opt_test.dart
index 5446171..e83757a 100644
--- a/tests/language/nullaware_opt_test.dart
+++ b/tests/language/nullaware_opt_test.dart
@@ -53,6 +53,12 @@
Expect.equals(201, e.f);
var x = 5 ?? bomb();
+ Expect.equals(5, x);
+
+ var y;
+ x = y ?? 1;
+ Expect.equals(1, x);
+ Expect.equals(null, y);
}
// Check that instructions without result do not crash.
diff --git a/tests/language/string_interpolation_and_buffer.dart b/tests/language/string_interpolation_and_buffer_test.dart
similarity index 85%
rename from tests/language/string_interpolation_and_buffer.dart
rename to tests/language/string_interpolation_and_buffer_test.dart
index ddcdd4e..5006d6e 100644
--- a/tests/language/string_interpolation_and_buffer.dart
+++ b/tests/language/string_interpolation_and_buffer_test.dart
@@ -5,6 +5,8 @@
// Test to ensure that StringBuffer and string interpolation behaves
// the same and fail fast.
+import "package:expect/expect.dart";
+
class ToStringWrapper {
final value;
@@ -15,9 +17,17 @@
wrap(value) => new ToStringWrapper(value);
+final bool checkedMode = computeCheckedMode();
+bool computeCheckedMode() {
+ try {
+ int x = "foo";
+ } on Error {
+ return true;
+ }
+ return false;
+}
+
main() {
- bool checkedMode = false;
- assert(checkedMode = true);
interpolate(object) {
var result;
if (checkedMode && object != null) {
@@ -25,6 +35,8 @@
result = '${wrap(object)}';
} on TypeError {
return 'Error';
+ } on ArgumentError {
+ return 'Error'; // Checked mode.
}
} else {
try {
@@ -44,6 +56,8 @@
sb = new StringBuffer()..write(wrap(object));
} on TypeError {
return 'Error';
+ } on ArgumentError {
+ return 'Error'; // Checked mode.
}
} else {
try {
@@ -63,6 +77,8 @@
sb = new StringBuffer(wrap(object));
} on TypeError {
return 'Error';
+ } on ArgumentError {
+ return 'Error'; // Checked mode.
}
} else {
try {
diff --git a/tests/language/string_split_test.dart b/tests/language/string_split_test.dart
new file mode 100644
index 0000000..56189cc
--- /dev/null
+++ b/tests/language/string_split_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2012, 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.
+// Issue 24043.
+
+import "package:expect/expect.dart";
+
+class EvilMatch implements Match {
+ int get start => 100000000;
+ int get end => 3;
+}
+
+class EvilIterator implements Iterator {
+ bool moveNext() => true;
+ EvilMatch get current => new EvilMatch();
+}
+
+class EvilIterable extends Iterable {
+ Iterator get iterator => new EvilIterator();
+}
+
+class EvilPattern implements Pattern {
+ Iterable allMatches(String s) => new EvilIterable();
+}
+
+void main() {
+ Expect.throws(() => "foo".split(new EvilPattern())[0].length,
+ (e) => e is RangeError);
+}
diff --git a/tools/VERSION b/tools/VERSION
index d45b9a4..3f0c944 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 12
PATCH 0
PRERELEASE 5
-PRERELEASE_PATCH 2
+PRERELEASE_PATCH 3