Version 1.12.0-dev.5.2
Cherry-pick 206083c8b1520c77e66c2eb138aa88fc45a49321 to dev
Cherry-pick 3cbedb02e418ccbad3b865ba5bd2ee558de1c7ef to dev
Cherry-pick 7e9fcbf5d6942ef14a287984ee72207469f34943 to dev
Cherry-pick 49daca25d0fe85ee47c88a959f0007b18e60d4f7 to dev
Cherry-pick 6377f1858726949354437381f596d82316c361dd to dev
Cherry-pick d71110f31fbfe1c17fbd516a111968e06dfffd7d to dev
Cherry-pick 1426ac4f77eb1669775baee49cee7ea5a79b7987 to dev
Cherry-pick f73b8aa019adcc1a443ceb0f61953853130fa528 to dev
Cherry-pick e974b952a73dffa72ed1ea87f5f8cd85f8790782 to dev
Cherry-pick 223e5a6bb5877dd4c3c46cf1f097172c879e8424 to dev
Cherry-pick ee9c0cbb2c86935bab2a31c241ebbbe01466e9e0 to dev
Cherry-pick b6c979f20b01239e92e7acc3ad34b879d4d95b83 to dev
Cherry-pick 227010d9dc84c2e5bf042ca1c0367f36eacbfa6b to dev
Cherry-pick 521dee763c95a374031e06572d3f56ceb8e8d91f to dev
Cherry-pick 38b9945cfd7cb2cc587df013d4d31ae028d99f6b to dev
Cherry-pick b34267d0d79d43bc343a26901f67a00a176f4bb8 to dev
Cherry-pick 49a61ba3f53a135cd7ce6085fb0afc642c50d123 to dev
Cherry-pick 8cee9ac89d92f702950c27e2982177ea1da5560b to dev
Cherry-pick 97a4ae8f9496b6740cff67b1664258b51c0a7f68 to dev
Cherry-pick 756763f3ca17737a464d8482303cebf068c7599d to dev
Cherry-pick 2b2d4cf1bf355648e218542e9c7d4641999fb5bc to dev
Cherry-pick 447e497ac985d7f13dfb1ad2d98a5b1c2e8fc709 to dev
Cherry-pick fc981a21c5b141aaf2526073988d7545ec851c16 to dev
Cherry-pick 5b6136fdfb0addf98abc52fff1c605a84bfd465c to dev
Cherry-pick a5c0dbfffe58ab3a568b4928da3e6a3b79315262 to dev
Cherry-pick 55b7315d8387268e43cac325106dd62b241b2c7f to dev
Cherry-pick e61f1dc8ce48789166121364bcbc961f45dc38be to dev
Cherry-pick 256b044857a44977ba1b4ca58d3d96ab39765bdf to dev
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 41fb266..b879a36 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -729,7 +729,7 @@
* dependencies (currently we only use it to track "pub list" dependencies).
*/
FolderDisposition _computeFolderDisposition(
- Folder folder, void addDependency(String path)) {
+ Folder folder, void addDependency(String path), File packagespecFile) {
String packageRoot = normalizedPackageRoots[folder.path];
if (packageRoot != null) {
// TODO(paulberry): We shouldn't be using JavaFile here because it
@@ -762,18 +762,28 @@
// resolve packages.
return new NoPackageFolderDisposition(packageRoot: packageRoot);
} else {
- callbacks.beginComputePackageMap();
- if (packageResolverProvider != null) {
- UriResolver resolver = packageResolverProvider(folder);
- if (resolver != null) {
- return new CustomPackageResolverDisposition(resolver);
- }
- }
PackageMapInfo packageMapInfo;
- ServerPerformanceStatistics.pub.makeCurrentWhile(() {
- packageMapInfo = _packageMapProvider.computePackageMap(folder);
- });
- callbacks.endComputePackageMap();
+ callbacks.beginComputePackageMap();
+ try {
+ if (ENABLE_PACKAGESPEC_SUPPORT) {
+ // Try .packages first.
+ if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
+ Packages packages = _readPackagespec(packagespecFile);
+ return new PackagesFileDisposition(packages);
+ }
+ }
+ if (packageResolverProvider != null) {
+ UriResolver resolver = packageResolverProvider(folder);
+ if (resolver != null) {
+ return new CustomPackageResolverDisposition(resolver);
+ }
+ }
+ ServerPerformanceStatistics.pub.makeCurrentWhile(() {
+ packageMapInfo = _packageMapProvider.computePackageMap(folder);
+ });
+ } finally {
+ callbacks.endComputePackageMap();
+ }
for (String dependencyPath in packageMapInfo.dependencies) {
addDependency(dependencyPath);
}
@@ -797,17 +807,10 @@
FolderDisposition disposition;
List<String> dependencies = <String>[];
- if (ENABLE_PACKAGESPEC_SUPPORT) {
- // Try .packages first.
- if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
- Packages packages = _readPackagespec(packagespecFile);
- disposition = new PackagesFileDisposition(packages);
- }
- }
-
// Next resort to a package uri resolver.
if (disposition == null) {
- disposition = _computeFolderDisposition(folder, dependencies.add);
+ disposition =
+ _computeFolderDisposition(folder, dependencies.add, packagespecFile);
}
info.setDependencies(dependencies);
@@ -832,18 +835,7 @@
ContextInfo parent, Folder folder, bool withPackageSpecOnly) {
// Decide whether a context needs to be created for [folder] here, and if
// so, create it.
- File packageSpec;
-
- if (ENABLE_PACKAGESPEC_SUPPORT) {
- // Start by looking for .packages.
- packageSpec = folder.getChild(PACKAGE_SPEC_NAME);
- }
-
- // Fall back to looking for a pubspec.
- if (packageSpec == null || !packageSpec.exists) {
- packageSpec = folder.getChild(PUBSPEC_NAME);
- }
-
+ File packageSpec = _findPackageSpecFile(folder);
bool createContext = packageSpec.exists || !withPackageSpecOnly;
if (withPackageSpecOnly &&
packageSpec.exists &&
@@ -928,6 +920,28 @@
}
/**
+ * Find the file that should be used to determine whether a context needs to
+ * be created here--this is either the ".packages" file or the "pubspec.yaml"
+ * file.
+ */
+ File _findPackageSpecFile(Folder folder) {
+ // Decide whether a context needs to be created for [folder] here, and if
+ // so, create it.
+ File packageSpec;
+
+ if (ENABLE_PACKAGESPEC_SUPPORT) {
+ // Start by looking for .packages.
+ packageSpec = folder.getChild(PACKAGE_SPEC_NAME);
+ }
+
+ // Fall back to looking for a pubspec.
+ if (packageSpec == null || !packageSpec.exists) {
+ packageSpec = folder.getChild(PUBSPEC_NAME);
+ }
+ return packageSpec;
+ }
+
+ /**
* Return the [ContextInfo] for the "innermost" context whose associated
* folder is or contains the given path. ("innermost" refers to the nesting
* of contexts, so if there is a context for path /foo and a context for
@@ -1178,8 +1192,8 @@
// while we're rerunning "pub list", since any analysis we complete while
// "pub list" is in progress is just going to get thrown away anyhow.
List<String> dependencies = <String>[];
- FolderDisposition disposition =
- _computeFolderDisposition(info.folder, dependencies.add);
+ FolderDisposition disposition = _computeFolderDisposition(
+ info.folder, dependencies.add, _findPackageSpecFile(info.folder));
info.setDependencies(dependencies);
callbacks.updateContextPackageUriResolver(info.folder, disposition);
}
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index d81f338..14b24a9 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -555,6 +555,20 @@
equals('/home/somebody/.pub/cache/unittest-0.9.9/lib/unittest.dart'));
}
+ void test_setRoots_addFolderWithPackagespecAndPackageRoot() {
+ // The package root should take priority.
+ String packagespecPath = posix.join(projPath, '.packages');
+ resourceProvider.newFile(packagespecPath,
+ 'unittest:file:///home/somebody/.pub/cache/unittest-0.9.9/lib/');
+ String packageRootPath = '/package/root/';
+ manager.setRoots(<String>[projPath], <String>[],
+ <String, String>{projPath: packageRootPath});
+ expect(callbacks.currentContextPaths, hasLength(1));
+ expect(callbacks.currentContextPaths, contains(projPath));
+ expect(callbacks.currentContextDispositions[projPath].packageRoot,
+ packageRootPath);
+ }
+
void test_setRoots_addFolderWithPubspec() {
String pubspecPath = posix.join(projPath, 'pubspec.yaml');
resourceProvider.newFile(pubspecPath, 'pubspec');
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index 808f88f1..123938d 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -93,6 +93,7 @@
showPackageWarnings:
hasOption(options, '--show-package-warnings'),
useContentSecurityPolicy: hasOption(options, '--csp'),
+ useStartupEmitter: hasOption(options, '--fast-startup'),
hasIncrementalSupport:
forceIncrementalSupport ||
hasOption(options, '--incremental-support'),
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 1ed9781..f635379 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -1056,6 +1056,7 @@
this.deferredMapUri: null,
this.dumpInfo: false,
this.showPackageWarnings: false,
+ bool useStartupEmitter: false,
this.useContentSecurityPolicy: false,
this.suppressWarnings: false,
this.fatalWarnings: false,
@@ -1100,7 +1101,8 @@
if (emitJavaScript) {
js_backend.JavaScriptBackend jsBackend =
new js_backend.JavaScriptBackend(
- this, generateSourceMap: generateSourceMap);
+ this, generateSourceMap: generateSourceMap,
+ useStartupEmitter: useStartupEmitter);
backend = jsBackend;
} else {
backend = new dart_backend.DartBackend(this, strips,
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 24a058f..72c40ab 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -317,6 +317,7 @@
new OptionHandler('--library-root=.+', setLibraryRoot),
new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
new OptionHandler('--allow-mock-compilation', passThrough),
+ new OptionHandler('--fast-startup', passThrough),
new OptionHandler('--minify|-m', implyCompilation),
new OptionHandler('--preserve-uris', passThrough),
new OptionHandler('--force-strip=.*', setStrip),
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 91c4765..d17fffd 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -622,7 +622,8 @@
final SourceInformationStrategy sourceInformationStrategy;
JavaScriptBackend(Compiler compiler,
- {bool generateSourceMap: true})
+ {bool generateSourceMap: true,
+ bool useStartupEmitter: false})
: namer = determineNamer(compiler),
oneShotInterceptors = new Map<jsAst.Name, Selector>(),
interceptedElements = new Map<String, Set<Element>>(),
@@ -636,7 +637,8 @@
: const StartEndSourceInformationStrategy())
: const JavaScriptSourceInformationStrategy(),
super(compiler) {
- emitter = new CodeEmitterTask(compiler, namer, generateSourceMap);
+ emitter = new CodeEmitterTask(
+ compiler, namer, generateSourceMap, useStartupEmitter);
typeVariableHandler = new TypeVariableHandler(compiler);
customElementsAnalysis = new CustomElementsAnalysis(this);
noSuchMethodRegistry = new NoSuchMethodRegistry(this);
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index b069aaf..bea4320 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -5,8 +5,6 @@
part of dart2js.js_emitter;
const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter");
-const USE_STARTUP_EMITTER =
- const bool.fromEnvironment("dart2js.use.startup.emitter");
/**
* Generates the code for all used classes in the program. Static fields (even
@@ -35,14 +33,15 @@
/// Contains a list of all classes that are emitted.
Set<ClassElement> neededClasses;
- CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap)
+ CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap,
+ bool useStartupEmitter)
: super(compiler),
this.namer = namer,
this.typeTestRegistry = new TypeTestRegistry(compiler) {
nativeEmitter = new NativeEmitter(this);
if (USE_LAZY_EMITTER) {
emitter = new lazy_js_emitter.Emitter(compiler, namer, nativeEmitter);
- } else if (USE_STARTUP_EMITTER) {
+ } else if (useStartupEmitter) {
emitter = new startup_js_emitter.Emitter(
compiler, namer, nativeEmitter, generateSourceMap);
} else {
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
index 01fda29..0bc38d3 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
@@ -13,28 +13,6 @@
typedef jsAst.Property AddPropertyFunction(jsAst.Name name,
jsAst.Expression value);
-const String GENERATED_BY = """
-// Generated by dart2js, the Dart to JavaScript compiler.
-""";
-
-const String HOOKS_API_USAGE = """
-// The code supports the following hooks:
-// dartPrint(message):
-// if this function is defined it is called instead of the Dart [print]
-// method.
-//
-// dartMainRunner(main, args):
-// if this function is defined, the Dart [main] method will not be invoked
-// directly. Instead, a closure that will invoke [main], and its arguments
-// [args] is passed to [dartMainRunner].
-//
-// dartDeferredLibraryLoader(uri, successCallback, errorCallback):
-// if this function is defined, it will be called when a deferered library
-// is loaded. It should load and eval the javascript of `uri`, and call
-// successCallback. If it fails to do so, it should call errorCallback with
-// an error.
-""";
-
// Compact field specifications. The format of the field specification is
// <accessorName>:<fieldName><suffix> where the suffix and accessor name
// prefix are optional. The suffix directs the generation of getter and
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
index 60382c1..319bc72 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
@@ -12,6 +12,8 @@
JsBuiltin,
JsGetName;
+import '../headers.dart';
+
import '../js_emitter.dart' hide Emitter;
import '../js_emitter.dart' as js_emitter show Emitter;
@@ -2072,11 +2074,10 @@
}
jsAst.Comment buildGeneratedBy() {
- String suffix = '';
- if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
- String msg = '// Generated by dart2js, the Dart to JavaScript '
- 'compiler$suffix.';
- return new jsAst.Comment(msg);
+ List<String> options = [];
+ if (compiler.mirrorsLibrary != null) options.add('mirrors');
+ if (compiler.useContentSecurityPolicy) options.add("CSP");
+ return new jsAst.Comment(generatedBy(compiler, flavor: options.join(", ")));
}
void outputSourceMap(CodeOutput output,
diff --git a/pkg/compiler/lib/src/js_emitter/headers.dart b/pkg/compiler/lib/src/js_emitter/headers.dart
new file mode 100644
index 0000000..7906dd9
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/headers.dart
@@ -0,0 +1,32 @@
+// 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.
+
+library dart2js.js_emitter.headers;
+import '../dart2jslib.dart' show Compiler;
+
+String generatedBy(Compiler compiler, {String flavor: ""}) {
+ String suffix = '';
+ if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
+ if (flavor != "") flavor = " ($flavor)";
+ return '// Generated by dart2js$flavor, '
+ 'the Dart to JavaScript compiler$suffix.';
+}
+
+const String HOOKS_API_USAGE = """
+// The code supports the following hooks:
+// dartPrint(message):
+// if this function is defined it is called instead of the Dart [print]
+// method.
+//
+// dartMainRunner(main, args):
+// if this function is defined, the Dart [main] method will not be invoked
+// directly. Instead, a closure that will invoke [main], and its arguments
+// [args] is passed to [dartMainRunner].
+//
+// dartDeferredLibraryLoader(uri, successCallback, errorCallback):
+// if this function is defined, it will be called when a deferered library
+// is loaded. It should load and eval the javascript of `uri`, and call
+// successCallback. If it fails to do so, it should call errorCallback with
+// an error.
+""";
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index fe83370..7e4703e 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -378,6 +378,9 @@
/// The element should only be used during the transition to the new model.
/// Uses indicate missing information in the model.
final Element element;
+ /// The name of the method. If the method is a [ParameterStubMethod] for a
+ /// static function, then the name can be `null`. In that case, only the
+ /// [ParameterStubMethod.callName] should be used.
final js.Name name;
final js.Expression code;
diff --git a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
index 92cf30e..44350b5 100644
--- a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
@@ -148,7 +148,7 @@
jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
- jsAst.Name name = namer.invocationName(selector);
+ jsAst.Name name = member.isStatic ? null : namer.invocationName(selector);
jsAst.Name callName =
(callSelector != null) ? namer.invocationName(callSelector) : null;
return new ParameterStubMethod(name, callName, function);
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index f0b5a78..c4e0012 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -518,9 +518,10 @@
js.Statement emitDeferredInitializerGlobal(Map loadMap) {
if (loadMap.isEmpty) return new js.Block.empty();
- return js.js.statement("""
- if (typeof(${ModelEmitter.deferredInitializersGlobal}) === 'undefined')
- var ${ModelEmitter.deferredInitializersGlobal} = Object.create(null);""");
+ String global = ModelEmitter.deferredInitializersGlobal;
+ return js.js.statement(
+ "if (typeof($global) === 'undefined') var # = Object.create(null);",
+ new js.VariableDeclaration(global, allowRename: false));
}
/// Emits all holders, except for the static-state holder.
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index e30c58b..ed24300 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -34,6 +34,7 @@
import '../../util/uri_extras.dart' show
relativize;
+import '../headers.dart';
import '../js_emitter.dart' show AstContainer, NativeEmitter;
import 'package:js_runtime/shared/embedded_names.dart' show
@@ -218,11 +219,11 @@
}
/// Generates a simple header that provides the compiler's build id.
- String buildGeneratedBy(compiler) {
- var suffix = '';
- if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
- return '// Generated by dart2js (fast startup), '
- 'the Dart to JavaScript compiler$suffix.\n';
+ js.Comment buildGeneratedBy() {
+ String flavor = compiler.useContentSecurityPolicy
+ ? 'fast startup, CSP'
+ : 'fast startup';
+ return new js.Comment(generatedBy(compiler, flavor: flavor));
}
/// Writes all deferred fragment's code into files.
@@ -258,7 +259,12 @@
codeOutputListeners);
outputBuffers[fragment] = mainOutput;
- mainOutput.addBuffer(js.prettyPrint(code, compiler,
+ js.Program program = new js.Program([
+ buildGeneratedBy(),
+ new js.Comment(HOOKS_API_USAGE),
+ code]);
+
+ mainOutput.addBuffer(js.prettyPrint(program, compiler,
monitor: compiler.dumpInfoTask));
if (shouldGenerateSourceMap) {
@@ -307,9 +313,11 @@
// deferredInitializer.current = <pretty-printed code>;
// deferredInitializer[<hash>] = deferredInitializer.current;
- output.add('\n${deferredInitializersGlobal}.current = ');
+ js.Program program = new js.Program([
+ buildGeneratedBy(),
+ js.js.statement('$deferredInitializersGlobal.current = #', code)]);
- output.addBuffer(js.prettyPrint(code, compiler,
+ output.addBuffer(js.prettyPrint(program, compiler,
monitor: compiler.dumpInfoTask));
// Make a unique hash of the code (before the sourcemaps are added)
diff --git a/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart b/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
index b29b582..9a2cbe2 100644
--- a/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
+++ b/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
@@ -18,6 +18,10 @@
[Map<Symbol, dynamic> namedArguments]) {
throw new UnsupportedError('ObjectMirror.invoke unsupported.');
}
+
+ delegate(Invocation invocation) {
+ throw new UnsupportedError('ObjectMirror.delegate unsupported');
+ }
}
abstract class InstanceMirrorMixin implements InstanceMirror {
@@ -26,10 +30,6 @@
get reflectee {
throw new UnsupportedError('InstanceMirror.reflectee unsupported.');
}
-
- delegate(Invocation invocation) {
- throw new UnsupportedError('InstanceMirror.delegate unsupported');
- }
}
InstanceMirror _convertConstantToInstanceMirror(
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart
index d678665..feb26a9 100644
--- a/runtime/bin/builtin.dart
+++ b/runtime/bin/builtin.dart
@@ -309,13 +309,15 @@
void _finishLoadRequest(_LoadRequest req) {
- // Now that we are done with loading remove the request from the map.
- var tmp = _reqMap.remove(req._id);
- assert(tmp == req);
- if (_traceLoading) {
- _log("Loading of ${req._uri} finished: "
- "${_reqMap.length} requests remaining, "
- "${_pendingPackageLoads.length} packages pending.");
+ if (req != null) {
+ // Now that we are done with loading remove the request from the map.
+ var tmp = _reqMap.remove(req._id);
+ assert(tmp == req);
+ if (_traceLoading) {
+ _log("Loading of ${req._uri} finished: "
+ "${_reqMap.length} requests remaining, "
+ "${_pendingPackageLoads.length} packages pending.");
+ }
}
if (!_pendingLoads()) {
@@ -447,6 +449,8 @@
// Reset the pending package loads to empty. So that we eventually can
// finish loading.
_pendingPackageLoads = [];
+ // Make sure that the receive port is closed if no other loads are pending.
+ _finishLoadRequest(null);
}
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 6c83fc8..e970d6e 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -12,7 +12,9 @@
#include "bin/file.h"
#include "bin/platform.h"
+#if !defined(TARGET_OS_IOS)
#include <crt_externs.h> // NOLINT
+#endif // !defined(TARGET_OS_IOS)
#include <signal.h> // NOLINT
#include <string.h> // NOLINT
#include <unistd.h> // NOLINT
diff --git a/runtime/bin/vmservice/loader.dart b/runtime/bin/vmservice/loader.dart
index 68f63d9..cd94f57 100644
--- a/runtime/bin/vmservice/loader.dart
+++ b/runtime/bin/vmservice/loader.dart
@@ -397,7 +397,7 @@
sp.send([packageRoot.toString()]);
}
} else {
- sp.send("Unsupported base URI to identify .packages file: "
+ sp.send("Unsupported scheme used to locate .packages file: "
"'$resource'.");
}
} else if (id == -2) {
diff --git a/runtime/lib/array_patch.dart b/runtime/lib/array_patch.dart
index 109938b..abe5a19 100644
--- a/runtime/lib/array_patch.dart
+++ b/runtime/lib/array_patch.dart
@@ -45,6 +45,10 @@
list.add(e);
}
if (growable) return list;
+ if (list.length == 0) {
+ // Avoid getting an immutable list from makeListFixedLength.
+ return new List<E>(0);
+ }
return makeListFixedLength(list);
}
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 24c9523..d68e758 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -1077,7 +1077,7 @@
Field& field = Field::Handle();
for (intptr_t i = 0; i < num_fields; i++) {
field ^= fields.At(i);
- if (!field.is_synthetic()) {
+ if (field.is_reflectable()) {
member_mirror = CreateVariableMirror(field, owner_mirror);
member_mirrors.Add(member_mirror);
}
@@ -1172,7 +1172,7 @@
}
} else if (entry.IsField()) {
const Field& field = Field::Cast(entry);
- if (!field.is_synthetic()) {
+ if (field.is_reflectable()) {
member_mirror = CreateVariableMirror(field, owner_mirror);
member_mirrors.Add(member_mirror);
}
@@ -1609,7 +1609,7 @@
return result.raw();
}
- if (field.is_final()) {
+ if (field.is_final() || !field.is_reflectable()) {
ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
internal_setter_name,
setter,
@@ -1906,7 +1906,7 @@
return result.raw();
}
- if (field.is_final()) {
+ if (field.is_final() || !field.is_reflectable()) {
ThrowNoSuchMethod(Instance::null_instance(),
internal_setter_name,
setter,
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index c64be5d..0c7b0bb 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -371,6 +371,25 @@
this._invokeSetter(_reflectee, _n(memberName), value);
return reflect(value);
}
+
+ delegate(Invocation invocation) {
+ if (invocation.isMethod) {
+ return this.invoke(invocation.memberName,
+ invocation.positionalArguments,
+ invocation.namedArguments).reflectee;
+ }
+ if (invocation.isGetter) {
+ return this.getField(invocation.memberName).reflectee;
+ }
+ if (invocation.isSetter) {
+ var unwrapped = _n(invocation.memberName);
+ var withoutEqual = _s(unwrapped.substring(0, unwrapped.length - 1));
+ var arg = invocation.positionalArguments[0];
+ this.setField(withoutEqual, arg).reflectee;
+ return arg;
+ }
+ throw "UNREACHABLE";
+ }
}
class _LocalInstanceMirror extends _LocalObjectMirror
@@ -393,25 +412,6 @@
get reflectee => _reflectee;
- delegate(Invocation invocation) {
- if (invocation.isMethod) {
- return this.invoke(invocation.memberName,
- invocation.positionalArguments,
- invocation.namedArguments).reflectee;
- }
- if (invocation.isGetter) {
- return this.getField(invocation.memberName).reflectee;
- }
- if (invocation.isSetter) {
- var unwrapped = _n(invocation.memberName);
- var withoutEqual = _s(unwrapped.substring(0, unwrapped.length - 1));
- var arg = invocation.positionalArguments[0];
- this.setField(withoutEqual, arg).reflectee;
- return arg;
- }
- throw "UNREACHABLE";
- }
-
String toString() => 'InstanceMirror on ${Error.safeToString(_reflectee)}';
bool operator ==(other) {
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart
index 60e720d..ddbfdc2 100644
--- a/runtime/observatory/lib/object_graph.dart
+++ b/runtime/observatory/lib/object_graph.dart
@@ -116,6 +116,14 @@
}
}
+ int get highUint32 {
+ return high * (1 << 24) + (mid >> 4);
+ }
+
+ int get lowUint32 {
+ return (mid & 0xF) * (1 << 28) + low;
+ }
+
bool get isZero {
return (high == 0) && (mid == 0) && (low == 0);
}
@@ -224,22 +232,8 @@
int get retainedSize => _graph._retainedSizes[_id];
ObjectVertex get dominator => new ObjectVertex._(_graph._doms[_id], _graph);
- int get shallowSize {
- var stream = new ReadStream(_graph._chunks);
- stream.position = _graph._positions[_id];
- stream.skipUnsigned(); // addr
- stream.readUnsigned(); // shallowSize
- return stream.clampedUint32;
- }
-
- int get vmCid {
- var stream = new ReadStream(_graph._chunks);
- stream.position = _graph._positions[_id];
- stream.skipUnsigned(); // addr
- stream.skipUnsigned(); // shallowSize
- stream.readUnsigned(); // cid
- return stream.clampedUint32;
- }
+ int get shallowSize => _graph._shallowSizes[_id];
+ int get vmCid => _graph._cids[_id];
get successors => new _SuccessorsIterable(_graph, _id);
@@ -247,11 +241,11 @@
// Note that everywhere else in this file, "address" really means an address
// scaled down by kObjectAlignment. They were scaled down so they would fit
// into Smis on the client.
- var stream = new ReadStream(_graph._chunks);
- stream.position = _graph._positions[_id];
- stream.readUnsigned();
- // Complicated way to do (high:mid:low * _kObjectAlignment).toHexString()
+ var high32 = _graph._addressesHigh[_id];
+ var low32 = _graph._addressesLow[_id];
+
+ // Complicated way to do (high:low * _kObjectAlignment).toHexString()
// without intermediate values exceeding int32.
var strAddr = "";
@@ -262,14 +256,13 @@
nibble = nibble & 0xF;
strAddr = nibble.toRadixString(16) + strAddr;
}
- combine28(twentyEightBits) {
- for (int shift = 0; shift < 28; shift += 4) {
- combine4((twentyEightBits >> shift) & 0xF);
+ combine32(thirtyTwoBits) {
+ for (int shift = 0; shift < 32; shift += 4) {
+ combine4((thirtyTwoBits >> shift) & 0xF);
}
}
- combine28(stream.low);
- combine28(stream.mid);
- combine28(stream.high);
+ combine32(low32);
+ combine32(high32);
return strAddr;
}
@@ -301,27 +294,23 @@
class _SuccessorsIterator implements Iterator<ObjectVertex> {
final ObjectGraph _graph;
- ReadStream _stream;
+ int _nextSuccIndex;
+ int _limitSuccIndex;
ObjectVertex current;
_SuccessorsIterator(this._graph, int id) {
- _stream = new ReadStream(this._graph._chunks);
- _stream.position = _graph._positions[id];
- _stream.skipUnsigned(); // addr
- _stream.skipUnsigned(); // shallowSize
- _stream.skipUnsigned(); // cid
+ _nextSuccIndex = _graph._firstSuccs[id];
+ _limitSuccIndex = _graph._firstSuccs[id + 1];
}
bool moveNext() {
- while (true) {
- _stream.readUnsigned();
- if (_stream.isZero) return false;
- var nextId = _graph._addrToId.get(_stream.high, _stream.mid, _stream.low);
- if (nextId == null) continue; // Reference to VM isolate's heap.
- current = new ObjectVertex._(nextId, _graph);
+ if (_nextSuccIndex < _limitSuccIndex) {
+ var succId = _graph._succs[_nextSuccIndex++];
+ current = new ObjectVertex._(succId, _graph);
return true;
}
+ return false;
}
}
@@ -379,8 +368,14 @@
// We build futures here instead of marking the steps as async to avoid the
// heavy lifting being inside a transformed method.
- statusReporter.add("Finding node positions...");
- await new Future(() => _buildPositions());
+ statusReporter.add("Remapping $_N objects...");
+ await new Future(() => _remapNodes());
+
+ statusReporter.add("Remapping $_E references...");
+ await new Future(() => _remapEdges());
+
+ _addrToId = null;
+ _chunks = null;
statusReporter.add("Finding post order...");
await new Future(() => _buildPostOrder());
@@ -404,29 +399,42 @@
return this;
}
- final List<ByteData> _chunks;
+ List<ByteData> _chunks;
int _kObjectAlignment;
int _N;
int _E;
int _size;
- AddressMapper _addrToId;
-
// Indexed by node id, with id 0 representing invalid/uninitialized.
- Uint32List _positions; // Position of the node in the snapshot.
+ // From snapshot.
+ Uint16List _cids;
+ Uint32List _shallowSizes;
+ Uint32List _firstSuccs;
+ Uint32List _succs;
+ Uint32List _addressesLow; // No Uint64List in Javascript.
+ Uint32List _addressesHigh;
+
+ // Intermediates.
+ AddressMapper _addrToId;
Uint32List _postOrderOrdinals; // post-order index -> id
Uint32List _postOrderIndices; // id -> post-order index
Uint32List _firstPreds; // Offset into preds.
Uint32List _preds;
+
+ // Outputs.
Uint32List _doms;
Uint32List _retainedSizes;
- void _buildPositions() {
+ void _remapNodes() {
var N = _N;
+ var E = 0;
var addrToId = new AddressMapper(N);
- var positions = new Uint32List(N + 1);
+ var addressesHigh = new Uint32List(N + 1);
+ var addressesLow = new Uint32List(N + 1);
+ var shallowSizes = new Uint32List(N + 1);
+ var cids = new Uint16List(N + 1);
var stream = new ReadStream(_chunks);
stream.readUnsigned();
@@ -434,14 +442,18 @@
var id = 1;
while (stream.pendingBytes > 0) {
- positions[id] = stream.position;
stream.readUnsigned(); // addr
addrToId.put(stream.high, stream.mid, stream.low, id);
- stream.skipUnsigned(); // shallowSize
- stream.skipUnsigned(); // cid
+ addressesHigh[id] = stream.highUint32;
+ addressesLow[id] = stream.lowUint32;
+ stream.readUnsigned(); // shallowSize
+ shallowSizes[id] = stream.clampedUint32;
+ stream.readUnsigned(); // cid
+ cids[id] = stream.clampedUint32;
stream.readUnsigned();
while (!stream.isZero) {
+ E++;
stream.readUnsigned();
}
id++;
@@ -451,15 +463,62 @@
var root = addrToId.get(0, 0, 0);
assert(root == 1);
+ _E = E;
_addrToId = addrToId;
- _positions = positions;
+ _addressesLow = addressesLow;
+ _addressesHigh = addressesHigh;
+ _shallowSizes = shallowSizes;
+ _cids = cids;
+ }
+
+ void _remapEdges() {
+ var N = _N;
+ var E = _E;
+ var addrToId = _addrToId;
+
+ var firstSuccs = new Uint32List(N + 2);
+ var succs = new Uint32List(E);
+
+ var stream = new ReadStream(_chunks);
+ stream.skipUnsigned(); // addr alignment
+
+ var id = 1, edge = 0;
+ while (stream.pendingBytes > 0) {
+ stream.skipUnsigned(); // addr
+ stream.skipUnsigned(); // shallowSize
+ stream.skipUnsigned(); // cid
+
+ firstSuccs[id] = edge;
+
+ stream.readUnsigned();
+ while (!stream.isZero) {
+ var childId = addrToId.get(stream.high, stream.mid, stream.low);
+ if (childId != null) {
+ succs[edge] = childId;
+ edge++;
+ } else {
+ // Reference into VM isolate's heap.
+ }
+ stream.readUnsigned();
+ }
+ id++;
+ }
+ firstSuccs[id] = edge; // Extra entry for cheap boundary detection.
+
+ assert(id == N + 1);
+ assert(edge <= E); // edge is smaller because E was computed before we knew
+ // if references pointed into the VM isolate
+
+ _E = edge;
+ _firstSuccs = firstSuccs;
+ _succs = succs;
}
void _buildPostOrder() {
var N = _N;
- var E = 0;
- var addrToId = _addrToId;
- var positions = _positions;
+ var E = _E;
+ var firstSuccs = _firstSuccs;
+ var succs = _succs;
var postOrderOrdinals = new Uint32List(N);
var postOrderIndices = new Uint32List(N + 1);
@@ -472,36 +531,24 @@
var root = 1;
stackNodes[0] = root;
-
- var stream = new ReadStream(_chunks);
- stream.position = positions[root];
- stream.skipUnsigned(); // addr
- stream.skipUnsigned(); // shallowSize
- stream.skipUnsigned(); // cid
- stackCurrentEdgePos[0] = stream.position;
+ stackCurrentEdgePos[0] = firstSuccs[root];
visited[root] = 1;
while (stackTop >= 0) {
var n = stackNodes[stackTop];
var edgePos = stackCurrentEdgePos[stackTop];
- stream.position = edgePos;
- stream.readUnsigned(); // childAddr
- if (!stream.isZero) {
- stackCurrentEdgePos[stackTop] = stream.position;
- var childId = addrToId.get(stream.high, stream.mid, stream.low);
- if (childId == null) continue; // Reference to VM isolate's heap.
- E++;
+ if (edgePos < firstSuccs[n + 1]) {
+ var childId = succs[edgePos];
+ edgePos++;
+ stackCurrentEdgePos[stackTop] = edgePos;
if (visited[childId] == 1) continue;
+ // Push child.
stackTop++;
stackNodes[stackTop] = childId;
-
- stream.position = positions[childId];
- stream.skipUnsigned(); // addr
- stream.skipUnsigned(); // shallowSize
- stream.skipUnsigned(); // cid
- stackCurrentEdgePos[stackTop] = stream.position; // i.e., first edge
+ edgePos = firstSuccs[childId];
+ stackCurrentEdgePos[stackTop] = edgePos;
visited[childId] = 1;
} else {
// Done with all children.
@@ -522,8 +569,8 @@
void _buildPredecessors() {
var N = _N;
var E = _E;
- var addrToId = _addrToId;
- var positions = _positions;
+ var firstSuccs = _firstSuccs;
+ var succs = _succs;
// This is first filled with the predecessor counts, then reused to hold the
// offset to the first predecessor (see alias below).
@@ -534,22 +581,9 @@
var preds = new Uint32List(E);
// Count predecessors of each node.
- var stream = new ReadStream(_chunks);
- for (var i = 1; i <= N; i++) {
- stream.position = positions[i];
- stream.skipUnsigned(); // addr
- stream.skipUnsigned(); // shallowSize
- stream.skipUnsigned(); // cid
- stream.readUnsigned(); // succAddr
- while (!stream.isZero) {
- var succId = addrToId.get(stream.high, stream.mid, stream.low);
- if (succId != null) {
- numPreds[succId]++;
- } else {
- // Reference to VM isolate's heap.
- }
- stream.readUnsigned(); // succAddr
- }
+ for (var succIndex = 0; succIndex < E; succIndex++) {
+ var succId = succs[succIndex];
+ numPreds[succId]++;
}
// Assign indices into predecessors array.
@@ -567,20 +601,14 @@
// Fill predecessors array.
for (var i = 1; i <= N; i++) {
- stream.position = positions[i];
- stream.skipUnsigned(); // addr
- stream.skipUnsigned(); // shallowSize
- stream.skipUnsigned(); // cid
- stream.readUnsigned(); // succAddr
- while (!stream.isZero) {
- var succId = addrToId.get(stream.high, stream.mid, stream.low);
- if (succId != null) {
- var predIndex = nextPreds[succId]++;
- preds[predIndex] = i;
- } else {
- // Reference to VM isolate's heap.
- }
- stream.readUnsigned(); // succAddr
+ var startSuccIndex = firstSuccs[i];
+ var limitSuccIndex = firstSuccs[i + 1];
+ for (var succIndex = startSuccIndex;
+ succIndex < limitSuccIndex;
+ succIndex++) {
+ var succId = succs[succIndex];
+ var predIndex = nextPreds[succId]++;
+ preds[predIndex] = i;
}
}
@@ -670,21 +698,17 @@
var N = _N;
var size = 0;
- var positions = _positions;
+ var shallowSizes = _shallowSizes;
var postOrderOrdinals = _postOrderOrdinals;
var doms = _doms;
- var retainedSizes = new Uint32List(N + 1);
+
+ // Sum shallow sizes.
+ for (var i = 1; i < N; i++) {
+ size += shallowSizes[i];
+ }
// Start with retained size as shallow size.
- var reader = new ReadStream(_chunks);
- for (var i = 1; i <= N; i++) {
- reader.position = positions[i];
- reader.skipUnsigned(); // addr
- reader.readUnsigned(); // shallowSize
- var shallowSize = reader.clampedUint32;
- retainedSizes[i] = shallowSize;
- size += shallowSize;
- }
+ var retainedSizes = new Uint32List.fromList(shallowSizes);
// In post order (bottom up), add retained size to dominator's retained
// size, skipping root.
diff --git a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
index a67b1db..237c154 100644
--- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
+++ b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
@@ -135,15 +135,6 @@
}
setCodeAttributes() {
- if (hasOptimizedCode()) {
- attributes.add('optimized');
- }
- if (hasUnoptimizedCode()) {
- attributes.add('unoptimized');
- }
- if (isInlined()) {
- attributes.add('inlined');
- }
}
}
diff --git a/runtime/observatory/lib/src/elements/class_view.dart b/runtime/observatory/lib/src/elements/class_view.dart
index 7ca6fb8..cfd535b 100644
--- a/runtime/observatory/lib/src/elements/class_view.dart
+++ b/runtime/observatory/lib/src/elements/class_view.dart
@@ -17,6 +17,10 @@
@observable ServiceMap instances;
@observable int retainedBytes;
@observable ObservableList mostRetained;
+ SampleBufferControlElement sampleBufferControlElement;
+ StackTraceTreeConfigElement stackTraceTreeConfigElement;
+ CpuProfileTreeElement cpuProfileTreeElement;
+
ClassViewElement.created() : super.created();
Future<ServiceObject> evaluate(String expression) {
@@ -47,7 +51,22 @@
}
void attached() {
+ super.attached();
+ sampleBufferControlElement =
+ shadowRoot.querySelector('#sampleBufferControl');
+ assert(sampleBufferControlElement != null);
+ sampleBufferControlElement.onSampleBufferUpdate = onSampleBufferChange;
+ sampleBufferControlElement.state =
+ SampleBufferControlElement.kNotLoadedState;
+ stackTraceTreeConfigElement =
+ shadowRoot.querySelector('#stackTraceTreeConfig');
+ assert(stackTraceTreeConfigElement != null);
+ stackTraceTreeConfigElement.onTreeConfigChange = onTreeConfigChange;
+ cpuProfileTreeElement = shadowRoot.querySelector('#cpuProfileTree');
+ assert(cpuProfileTreeElement != null);
+ cpuProfileTreeElement.profile = sampleBufferControlElement.profile;
cls.fields.forEach((field) => field.reload());
+ sampleBufferControlElement.allocationProfileClass = cls;
}
Future refresh() {
@@ -64,17 +83,26 @@
return cls.refreshCoverage();
}
- final CpuProfile profile = new CpuProfile();
+ onSampleBufferChange(CpuProfile sampleBuffer) {
+ cpuProfileTreeElement.render();
+ }
+
+ onTreeConfigChange(String modeSelector, String directionSelector) {
+ ProfileTreeDirection direction = ProfileTreeDirection.Exclusive;
+ if (directionSelector != 'Up') {
+ direction = ProfileTreeDirection.Inclusive;
+ }
+ ProfileTreeMode mode = ProfileTreeMode.Function;
+ if (modeSelector == 'Code') {
+ mode = ProfileTreeMode.Code;
+ }
+ cpuProfileTreeElement.direction = direction;
+ cpuProfileTreeElement.mode = mode;
+ cpuProfileTreeElement.render();
+ }
Future refreshAllocationProfile() async {
- var profileResponse = await cls.getAllocationSamples('UserVM');
- profile.load(profileResponse.isolate, profileResponse);
- CpuProfileTreeElement cpuProfileTreeElement =
- shadowRoot.querySelector('#cpuProfileTree');
- cpuProfileTreeElement.profile = profile;
- cpuProfileTreeElement.direction = ProfileTreeDirection.Exclusive;
- cpuProfileTreeElement.mode = ProfileTreeMode.Function;
- cpuProfileTreeElement.render();
+ return sampleBufferControlElement.reload(cls.isolate);
}
Future toggleAllocationTrace() {
diff --git a/runtime/observatory/lib/src/elements/class_view.html b/runtime/observatory/lib/src/elements/class_view.html
index beddb21..200c22a 100644
--- a/runtime/observatory/lib/src/elements/class_view.html
+++ b/runtime/observatory/lib/src/elements/class_view.html
@@ -229,6 +229,10 @@
</div>
</template>
</div>
+ <sample-buffer-control id="sampleBufferControl"></sample-buffer-control>
+ <br>
+ <stack-trace-tree-config id="stackTraceTreeConfig"></stack-trace-tree-config>
+ <br>
<div class="flex-row centered">
<div class="flex-item-90-percent outlined" style="margin: 16px; margin-left: 8px; margin-right: 8px">
<cpu-profile-tree id="cpuProfileTree"></cpu-profile-tree>
diff --git a/runtime/observatory/lib/src/elements/cpu_profile.dart b/runtime/observatory/lib/src/elements/cpu_profile.dart
index bb5a626..e1d6d47 100644
--- a/runtime/observatory/lib/src/elements/cpu_profile.dart
+++ b/runtime/observatory/lib/src/elements/cpu_profile.dart
@@ -448,8 +448,14 @@
}
await _changeState(kFetchingState);
try {
- var params = { 'tags': tagSelector };
- var response = await isolate.invokeRpc('_getCpuProfile', params);
+ var response;
+ if (allocationProfileClass != null) {
+ response =
+ await allocationProfileClass.getAllocationSamples(tagSelector);
+ } else {
+ var params = { 'tags': tagSelector };
+ response = await isolate.invokeRpc('_getCpuProfile', params);
+ }
await _changeState(kLoadingState);
profile.load(isolate, response);
_update(profile);
@@ -512,7 +518,7 @@
@observable String fetchTime = '';
@observable String loadTime = '';
@observable String tagSelector = 'UserVM';
- @observable String state = 'kFetching';
+ @observable String state = kFetchingState;
@observable var exception;
@observable var stackTrace;
@@ -521,8 +527,10 @@
static const kFetchingState = 'kFetching';
static const kLoadedState = 'kLoaded';
static const kLoadingState = 'kLoading';
+ static const kNotLoadedState = 'kNotLoaded';
Isolate isolate;
+ Class allocationProfileClass;
final CpuProfile profile = new CpuProfile();
final Stopwatch _stopWatch = new Stopwatch();
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
index ae0ab6f..2034b95 100644
--- a/runtime/observatory/lib/src/elements/debugger.dart
+++ b/runtime/observatory/lib/src/elements/debugger.dart
@@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:html';
import 'observatory_element.dart';
+import 'package:observatory/app.dart';
import 'package:observatory/cli.dart';
import 'package:observatory/debugger.dart';
import 'package:observatory/service.dart';
@@ -27,7 +28,9 @@
// TODO(turnidge): Rewrite HelpCommand so that it is a general utility
// provided by the cli library.
class HelpCommand extends DebuggerCommand {
- HelpCommand(Debugger debugger) : super(debugger, 'help', []);
+ HelpCommand(Debugger debugger) : super(debugger, 'help', [
+ new HelpHotkeysCommand(debugger),
+ ]);
String _nameAndAlias(Command cmd) {
if (cmd.alias == null) {
@@ -50,6 +53,7 @@
}
con.print(
"\nFor more information on a specific command type 'help <command>'\n"
+ "For a list of hotkeys type 'help hotkeys'\n"
"\n"
"Command prefixes are accepted (e.g. 'h' for 'help')\n"
"Hit [TAB] to complete a command (try 'is[TAB][TAB]')\n"
@@ -105,6 +109,38 @@
' help <command> - Help for a specific command\n';
}
+class HelpHotkeysCommand extends DebuggerCommand {
+ HelpHotkeysCommand(Debugger debugger) : super(debugger, 'hotkeys', []);
+
+ Future run(List<String> args) {
+ var con = debugger.console;
+ con.print("List of hotkeys:\n"
+ "\n"
+ "[TAB] - complete a command\n"
+ "[Up Arrow] - history previous\n"
+ "[Down Arrow] - history next\n"
+ "\n"
+ "[Page Up] - move up one frame\n"
+ "[Page Down] - move down one frame\n"
+ "\n"
+ "[F7] - continue execution of the current isolate\n"
+ "[Ctrl ;] - pause execution of the current isolate\n"
+ "\n"
+ "[F8] - toggle breakpoint at current location\n"
+ "[F9] - next\n"
+ "[F10] - step\n"
+ "\n");
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Provide a list of hotkeys';
+
+ String helpLong =
+ 'Provide a list of key hotkeys.\n'
+ '\n'
+ 'Syntax: help hotkeys\n';
+}
+
class PrintCommand extends DebuggerCommand {
PrintCommand(Debugger debugger) : super(debugger, 'print', []) {
alias = 'p';
@@ -155,7 +191,7 @@
return new Future.value(null);
}
try {
- debugger.currentFrame -= count;
+ debugger.downFrame(count);
debugger.console.print('frame = ${debugger.currentFrame}');
} catch (e) {
debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
@@ -163,11 +199,13 @@
return new Future.value(null);
}
- String helpShort = 'Move down one or more frames';
+ String helpShort = 'Move down one or more frames (hotkey: [Page Down])';
String helpLong =
'Move down one or more frames.\n'
'\n'
+ 'Hotkey: [Page Down]\n'
+ '\n'
'Syntax: down\n'
' down <count>\n';
}
@@ -188,7 +226,7 @@
return new Future.value(null);
}
try {
- debugger.currentFrame += count;
+ debugger.upFrame(count);
debugger.console.print('frame = ${debugger.currentFrame}');
} on RangeError catch (e) {
debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
@@ -196,11 +234,13 @@
return new Future.value(null);
}
- String helpShort = 'Move up one or more frames';
+ String helpShort = 'Move up one or more frames (hotkey: [Page Up])';
String helpLong =
'Move up one or more frames.\n'
'\n'
+ 'Hotkey: [Page Up]\n'
+ '\n'
'Syntax: up\n'
' up <count>\n';
}
@@ -244,19 +284,16 @@
PauseCommand(Debugger debugger) : super(debugger, 'pause', []);
Future run(List<String> args) {
- if (!debugger.isolatePaused()) {
- return debugger.isolate.pause();
- } else {
- debugger.console.print('The program is already paused');
- return new Future.value(null);
- }
+ return debugger.pause();
}
- String helpShort = 'Pause the isolate';
+ String helpShort = 'Pause the isolate (hotkey: [Ctrl ;])';
String helpLong =
'Pause the isolate.\n'
'\n'
+ 'Hotkey: [Ctrl ;]\n'
+ '\n'
'Syntax: pause\n';
}
@@ -266,21 +303,16 @@
}
Future run(List<String> args) {
- if (debugger.isolatePaused()) {
- return debugger.isolate.resume().then((_) {
- debugger.warnOutOfDate();
- });
- } else {
- debugger.console.print('The program must be paused');
- return new Future.value(null);
- }
+ return debugger.resume();
}
- String helpShort = 'Resume execution of the isolate';
+ String helpShort = 'Resume execution of the isolate (hotkey: [F7])';
String helpLong =
'Continue running the isolate.\n'
'\n'
+ 'Hotkey: [F7]\n'
+ '\n'
'Syntax: continue\n'
' c\n';
}
@@ -289,31 +321,19 @@
NextCommand(Debugger debugger) : super(debugger, 'next', []);
Future run(List<String> args) {
- if (debugger.isolatePaused()) {
- var event = debugger.isolate.pauseEvent;
- if (event.kind == ServiceEvent.kPauseStart) {
- debugger.console.print("Type 'continue' to start the isolate");
- return new Future.value(null);
- }
- if (event.kind == ServiceEvent.kPauseExit) {
- debugger.console.print("Type 'continue' to exit the isolate");
- return new Future.value(null);
- }
- return debugger.isolate.stepOver();
- } else {
- debugger.console.print('The program is already running');
- return new Future.value(null);
- }
+ return debugger.next();
}
String helpShort =
'Continue running the isolate until it reaches the next source location '
- 'in the current function';
+ 'in the current function (hotkey: [F9])';
String helpLong =
'Continue running the isolate until it reaches the next source location '
'in the current function.\n'
'\n'
+ 'Hotkey: [F9]\n'
+ '\n'
'Syntax: next\n';
}
@@ -323,33 +343,39 @@
}
Future run(List<String> args) {
- if (debugger.isolatePaused()) {
- var event = debugger.isolate.pauseEvent;
- if (event.kind == ServiceEvent.kPauseStart) {
- debugger.console.print("Type 'continue' to start the isolate");
- return new Future.value(null);
- }
- if (event.kind == ServiceEvent.kPauseExit) {
- debugger.console.print("Type 'continue' to exit the isolate");
- return new Future.value(null);
- }
- return debugger.isolate.stepInto();
- } else {
- debugger.console.print('The program is already running');
- return new Future.value(null);
- }
+ return debugger.step();
}
String helpShort =
- 'Continue running the isolate until it reaches the next source location';
+ 'Continue running the isolate until it reaches the next source location'
+ ' (hotkey: [F10]';
String helpLong =
'Continue running the isolate until it reaches the next source '
'location.\n'
'\n'
+ 'Hotkey: [F10]\n'
+ '\n'
'Syntax: step\n';
}
+class ClsCommand extends DebuggerCommand {
+ ClsCommand(Debugger debugger) : super(debugger, 'cls', []) {}
+
+ Future run(List<String> args) {
+ debugger.console.clear();
+ debugger.console.newline();
+ return new Future.value(null);
+ }
+
+ String helpShort = 'Clear the console';
+
+ String helpLong =
+ 'Clear the console.\n'
+ '\n'
+ 'Syntax: cls\n';
+}
+
class LogCommand extends DebuggerCommand {
LogCommand(Debugger debugger) : super(debugger, 'log', []);
@@ -451,6 +477,16 @@
Future run(List<String> args) {
if (debugger.isolatePaused()) {
+ var event = debugger.isolate.pauseEvent;
+ if (event.kind == ServiceEvent.kPauseStart) {
+ debugger.console.print(
+ "Type 'continue' [F7] or 'step' [F10] to start the isolate");
+ return new Future.value(null);
+ }
+ if (event.kind == ServiceEvent.kPauseExit) {
+ debugger.console.print("Type 'continue' [F7] to exit the isolate");
+ return new Future.value(null);
+ }
return debugger.isolate.stepOut();
} else {
debugger.console.print('The program is already running');
@@ -471,29 +507,119 @@
SetCommand(Debugger debugger)
: super(debugger, 'set', []);
- Future run(List<String> args) async {
- if (args.length == 2) {
- var option = args[0].trim();
- if (option == 'break-on-exceptions') {
- var result = await debugger.isolate.setExceptionPauseInfo(args[1]);
- if (result.isError) {
- debugger.console.print(result.toString());
- }
- } else {
- debugger.console.print("unknown option '$option'");
- }
+ static var _boeValues = ['all', 'none', 'unhandled'];
+ static var _boolValues = ['false', 'true'];
+
+ static var _options = {
+ 'break-on-exception': [_boeValues,
+ _setBreakOnException,
+ (debugger, _) => debugger.breakOnException],
+ 'up-is-down': [_boolValues,
+ _setUpIsDown,
+ (debugger, _) => debugger.upIsDown],
+ };
+
+ static Future _setBreakOnException(debugger, name, value) async {
+ var result = await debugger.isolate.setExceptionPauseInfo(value);
+ if (result.isError) {
+ debugger.console.print(result.toString());
} else {
- debugger.console.print("set expects 2 arguments");
+ // Printing will occur elsewhere.
+ debugger.breakOnException = value;
}
}
+ static Future _setUpIsDown(debugger, name, value) async {
+ if (value == 'true') {
+ debugger.upIsDown = true;
+ } else {
+ debugger.upIsDown = false;
+ }
+ debugger.console.print('${name} = ${value}');
+ }
+
+ Future run(List<String> args) async {
+ if (args.length == 0) {
+ for (var name in _options.keys) {
+ var getHandler = _options[name][2];
+ var value = await getHandler(debugger, name);
+ debugger.console.print("${name} = ${value}");
+ }
+ } else if (args.length == 1) {
+ var name = args[0].trim();
+ var optionInfo = _options[name];
+ if (optionInfo == null) {
+ debugger.console.print("unrecognized option: $name");
+ return;
+ } else {
+ var getHandler = optionInfo[2];
+ var value = await getHandler(debugger, name);
+ debugger.console.print("${name} = ${value}");
+ }
+ } else if (args.length == 2) {
+ var name = args[0].trim();
+ var value = args[1].trim();
+ var optionInfo = _options[name];
+ if (optionInfo == null) {
+ debugger.console.print("unrecognized option: $name");
+ return;
+ }
+ var validValues = optionInfo[0];
+ if (!validValues.contains(value)) {
+ debugger.console.print("'${value}' is not in ${validValues}");
+ return;
+ }
+ var setHandler = optionInfo[1];
+ await setHandler(debugger, name, value);
+ } else {
+ debugger.console.print("set expects 0, 1, or 2 arguments");
+ }
+ }
+
+ Future<List<String>> complete(List<String> args) {
+ if (args.length < 1 || args.length > 2) {
+ return new Future.value([args.join('')]);
+ }
+ var result = [];
+ if (args.length == 1) {
+ var prefix = args[0];
+ for (var option in _options.keys) {
+ if (option.startsWith(prefix)) {
+ result.add('${option} ');
+ }
+ }
+ }
+ if (args.length == 2) {
+ var name = args[0].trim();
+ var prefix = args[1];
+ var optionInfo = _options[name];
+ if (optionInfo != null) {
+ var validValues = optionInfo[0];
+ for (var value in validValues) {
+ if (value.startsWith(prefix)) {
+ result.add('${args[0]}${value} ');
+ }
+ }
+ }
+ }
+ return new Future.value(result);
+ }
+
String helpShort =
'Set a debugger option';
String helpLong =
- 'Set a debugger option'
+ 'Set a debugger option.\n'
'\n'
- 'Syntax: set break-on-exceptions "all" | "none" | "unhandled"\n';
+ 'Known options:\n'
+ ' break-on-exceptions # Should the debugger break on exceptions?\n'
+ " # ${_boeValues}\n"
+ ' up-is-down # Reverse meaning of up/down commands?\n'
+ " # ${_boolValues}\n"
+ '\n'
+ 'Syntax: set # Display all option settings\n'
+ ' set <option> # Get current value for option\n'
+ ' set <option> <value> # Set value for option';
}
class BreakCommand extends DebuggerCommand {
@@ -548,11 +674,14 @@
return new Future.value(DebuggerLocation.complete(debugger, args[0]));
}
- String helpShort = 'Add a breakpoint by source location or function name';
+ String helpShort = 'Add a breakpoint by source location or function name'
+ ' (hotkey: [F8])';
String helpLong =
'Add a breakpoint by source location or function name.\n'
'\n'
+ 'Hotkey: [F8]\n'
+ '\n'
'Syntax: break '
'# Break at the current position\n'
' break <line> '
@@ -631,11 +760,14 @@
return new Future.value(DebuggerLocation.complete(debugger, args[0]));
}
- String helpShort = 'Remove a breakpoint by source location or function name';
+ String helpShort = 'Remove a breakpoint by source location or function name'
+ ' (hotkey: [F8])';
String helpLong =
'Remove a breakpoint by source location or function name.\n'
'\n'
+ 'Hotkey: [F8]\n'
+ '\n'
'Syntax: clear '
'# Clear at the current position\n'
' clear <line> '
@@ -805,17 +937,15 @@
Future<List<String>> complete(List<String> args) {
if (args.length != 1) {
return new Future.value([args.join('')]);
- }
- var isolates = debugger.vm.isolates.toList();
- isolates.sort((a, b) => a.startTime.compareTo(b.startTime));
+ }
var result = [];
- for (var isolate in isolates) {
+ for (var isolate in debugger.vm.isolates) {
var str = isolate.number.toString();
if (str.startsWith(args[0])) {
result.add('$str ');
}
}
- for (var isolate in isolates) {
+ for (var isolate in debugger.vm.isolates) {
if (isolate.name.startsWith(args[0])) {
result.add('${isolate.name} ');
}
@@ -840,9 +970,7 @@
"Internal error: vm has not been set");
return new Future.value(null);
}
- var isolates = debugger.vm.isolates.toList();
- isolates.sort((a, b) => a.startTime.compareTo(b.startTime));
- for (var isolate in isolates) {
+ for (var isolate in debugger.vm.isolates) {
String current = (isolate == debugger.isolate ? ' *' : '');
debugger.console.print(
"Isolate ${isolate.number} '${isolate.name}'${current}");
@@ -1027,15 +1155,17 @@
// Tracks the state for an isolate debugging session.
class ObservatoryDebugger extends Debugger {
+ final SettingsGroup settings = new SettingsGroup('debugger');
RootCommand cmd;
DebuggerPageElement page;
DebuggerConsoleElement console;
DebuggerInputElement input;
DebuggerStackElement stackElement;
ServiceMap stack;
- String exceptions = "none"; // Last known setting.
+ String breakOnException = "none"; // Last known setting.
int get currentFrame => _currentFrame;
+
void set currentFrame(int value) {
if (value != null && (value < 0 || value >= stackDepth)) {
throw new RangeError.range(value, 0, stackDepth);
@@ -1047,9 +1177,33 @@
}
int _currentFrame = null;
+ bool get upIsDown => _upIsDown;
+ void set upIsDown(bool value) {
+ settings.set('up-is-down', value);
+ _upIsDown = value;
+ }
+ bool _upIsDown;
+
+ void upFrame(int count) {
+ if (_upIsDown) {
+ currentFrame += count;
+ } else {
+ currentFrame -= count;
+ }
+ }
+
+ void downFrame(int count) {
+ if (_upIsDown) {
+ currentFrame -= count;
+ } else {
+ currentFrame += count;
+ }
+ }
+
int get stackDepth => stack['frames'].length;
ObservatoryDebugger() {
+ _loadSettings();
cmd = new RootCommand([
new HelpCommand(this),
new PrintCommand(this),
@@ -1070,19 +1224,24 @@
new IsolateCommand(this),
new RefreshCommand(this),
new LogCommand(this),
+ new ClsCommand(this),
]);
_consolePrinter = new _ConsoleStreamPrinter(this);
}
+ void _loadSettings() {
+ _upIsDown = settings.get('up-is-down');
+ }
+
VM get vm => page.app.vm;
void updateIsolate(Isolate iso) {
_isolate = iso;
if (_isolate != null) {
- if ((exceptions != iso.exceptionsPauseInfo) &&
+ if ((breakOnException != iso.exceptionsPauseInfo) &&
(iso.exceptionsPauseInfo != null)) {
- exceptions = iso.exceptionsPauseInfo;
- console.print("Now pausing for $exceptions exceptions");
+ breakOnException = iso.exceptionsPauseInfo;
+ console.print("Now pausing for exceptions: $breakOnException");
}
_isolate.reload().then((response) {
@@ -1190,10 +1349,10 @@
void _reportPause(ServiceEvent event) {
if (event.kind == ServiceEvent.kPauseStart) {
console.print(
- "Paused at isolate start (type 'continue' to start the isolate')");
+ "Paused at isolate start (type 'continue' [F7] or 'step' [F10] to start the isolate')");
} else if (event.kind == ServiceEvent.kPauseExit) {
console.print(
- "Paused at isolate exit (type 'continue' to exit the isolate')");
+ "Paused at isolate exit (type 'continue' or [F7] to exit the isolate')");
}
if (stack['frames'].length > 0) {
Frame frame = stack['frames'][0];
@@ -1277,9 +1436,9 @@
break;
case ServiceEvent.kDebuggerSettingsUpdate:
- if (exceptions != event.exceptions) {
- exceptions = event.exceptions;
- console.print("Now pausing for $exceptions exceptions");
+ if (breakOnException != event.exceptions) {
+ breakOnException = event.exceptions;
+ console.print("Now pausing for exceptions: $breakOnException");
}
break;
@@ -1416,6 +1575,80 @@
String historyNext(String command) {
return cmd.historyNext(command);
}
+
+ Future pause() {
+ if (!isolatePaused()) {
+ return isolate.pause();
+ } else {
+ console.print('The program is already paused');
+ return new Future.value(null);
+ }
+ }
+
+ Future resume() {
+ if (isolatePaused()) {
+ return isolate.resume().then((_) {
+ warnOutOfDate();
+ });
+ } else {
+ console.print('The program must be paused');
+ return new Future.value(null);
+ }
+ }
+
+ Future toggleBreakpoint() async {
+ var loc = await DebuggerLocation.parse(this, '');
+ var script = loc.script;
+ var line = loc.line;
+ if (script != null && line != null) {
+ var bpts = script.getLine(line).breakpoints;
+ if (bpts == null || bpts.isEmpty) {
+ // Set a new breakpoint.
+ // TODO(turnidge): Set this breakpoint at current column.
+ await isolate.addBreakpoint(script, line);
+ } else {
+ // TODO(turnidge): Clear this breakpoint at current column.
+ var pending = [];
+ for (var bpt in bpts) {
+ pending.add(isolate.removeBreakpoint(bpt));
+ }
+ await Future.wait(pending);
+ }
+ }
+ return new Future.value(null);
+ }
+
+ Future next() {
+ if (isolatePaused()) {
+ var event = isolate.pauseEvent;
+ if (event.kind == ServiceEvent.kPauseStart) {
+ console.print("Type 'continue' [F7] or 'step' [F10] to start the isolate");
+ return new Future.value(null);
+ }
+ if (event.kind == ServiceEvent.kPauseExit) {
+ console.print("Type 'continue' [F7] to exit the isolate");
+ return new Future.value(null);
+ }
+ return isolate.stepOver();
+ } else {
+ console.print('The program is already running');
+ return new Future.value(null);
+ }
+ }
+
+ Future step() {
+ if (isolatePaused()) {
+ var event = isolate.pauseEvent;
+ if (event.kind == ServiceEvent.kPauseExit) {
+ console.print("Type 'continue' [F7] to exit the isolate");
+ return new Future.value(null);
+ }
+ return isolate.stepInto();
+ } else {
+ console.print('The program is already running');
+ return new Future.value(null);
+ }
+ }
}
@CustomTag('debugger-page')
@@ -1484,7 +1717,8 @@
// Random clicks should focus on the text box. If the user selects
// a range, don't interfere.
var selection = window.getSelection();
- if (selection == null || selection.type == 'Caret') {
+ if (selection == null ||
+ (selection.type != 'Range' && selection.type != 'text')) {
debugger.input.focus();
}
});
@@ -1947,6 +2181,11 @@
void newline() {
_append(new BRElement());
}
+
+ void clear() {
+ var consoleTextElement = $['consoleText'];
+ consoleTextElement.children.clear();
+ }
}
@CustomTag('debugger-input')
@@ -2000,6 +2239,66 @@
busy = false;
break;
+ case KeyCode.PAGE_UP:
+ e.preventDefault();
+ try {
+ debugger.upFrame(1);
+ } on RangeError catch (e) {
+ // Ignore.
+ }
+ busy = false;
+ break;
+
+ case KeyCode.PAGE_DOWN:
+ e.preventDefault();
+ try {
+ debugger.downFrame(1);
+ } on RangeError catch (e) {
+ // Ignore.
+ }
+ busy = false;
+ break;
+
+ case KeyCode.F7:
+ e.preventDefault();
+ debugger.resume().whenComplete(() {
+ busy = false;
+ });
+ break;
+
+ case KeyCode.F8:
+ e.preventDefault();
+ debugger.toggleBreakpoint().whenComplete(() {
+ busy = false;
+ });
+ break;
+
+ case KeyCode.F9:
+ e.preventDefault();
+ debugger.next().whenComplete(() {
+ busy = false;
+ });
+ break;
+
+ case KeyCode.F10:
+ e.preventDefault();
+ debugger.step().whenComplete(() {
+ busy = false;
+ });
+ break;
+
+ case KeyCode.SEMICOLON:
+ if (e.ctrlKey) {
+ e.preventDefault();
+ debugger.console.printRed('^;');
+ debugger.pause().whenComplete(() {
+ busy = false;
+ });
+ } else {
+ busy = false;
+ }
+ break;
+
default:
busy = false;
break;
diff --git a/runtime/observatory/lib/src/elements/eval_box.dart b/runtime/observatory/lib/src/elements/eval_box.dart
index 957e74a..7447aca 100644
--- a/runtime/observatory/lib/src/elements/eval_box.dart
+++ b/runtime/observatory/lib/src/elements/eval_box.dart
@@ -17,6 +17,7 @@
class EvalBoxElement extends ObservatoryElement {
@observable String text;
@observable String lineMode = "1-line";
+ int _exprCount = 0;
@published evalType callback;
@observable ObservableList results = toObservable([]);
@@ -39,6 +40,7 @@
// Use provided callback to eval the expression.
if (callback != null) {
var map = toObservable({});
+ map['id'] = (_exprCount++).toString();
map['expr'] = expr;
results.insert(0, map);
callback(expr).then((result) {
@@ -56,5 +58,12 @@
text = targetElement.getAttribute('expr');
}
+ void closeItem(MouseEvent e) {
+ assert(e.target is Element);
+ Element targetElement = e.target;
+ var closeId = targetElement.getAttribute('closeId');
+ results.removeWhere((item) => item['id'] == closeId);
+ }
+
EvalBoxElement.created() : super.created();
}
diff --git a/runtime/observatory/lib/src/elements/eval_box.html b/runtime/observatory/lib/src/elements/eval_box.html
index a6d61d3..ad9045a 100644
--- a/runtime/observatory/lib/src/elements/eval_box.html
+++ b/runtime/observatory/lib/src/elements/eval_box.html
@@ -42,6 +42,22 @@
display: block;
padding: 6px 6px;
}
+ a.boxclose {
+ margin-left: 20px;
+ valign: top;
+ display: block;
+ height: 18px;
+ width: 18px;
+ line-height: 16px;
+ border-radius: 9px;
+ color: black;
+ font-size: 18px;
+ cursor: pointer;
+ text-align: center;
+ }
+ a.boxclose:hover {
+ background: lightgray;
+ }
</style>
<form style="display:flex; flex-direction:row; align-items:flex-end">
<template if="{{ lineMode == '1-line' }}">
@@ -86,6 +102,10 @@
</template>
</template>
</td>
+ <td>
+ <a class="boxclose" on-click="{{ closeItem }}"
+ closeId="{{ result['id'] }}">×</a>
+ </td>
</tr>
</table>
</template>
diff --git a/runtime/observatory/lib/src/elements/library_view.html b/runtime/observatory/lib/src/elements/library_view.html
index e28ebad..a898ebb 100644
--- a/runtime/observatory/lib/src/elements/library_view.html
+++ b/runtime/observatory/lib/src/elements/library_view.html
@@ -59,7 +59,18 @@
<template repeat="{{ dep in library.dependencies }}">
<div class="memberItem">
<div class="memberValue">
- <library-ref ref="{{ dep.target }}"></library-ref>
+ <template if="{{ dep.isImport }}">
+ import <library-ref ref="{{ dep.target }}"></library-ref>
+ <template if="{{ dep.prefix != null }}">
+ as {{ dep.prefix.toString() }}
+ </template>
+ <template if="{{ dep.isDeferred }}">
+ deferred
+ </template>
+ </template>
+ <template if="{{ !dep.isImport }}">
+ export <library-ref ref="{{ dep.target }}"></library-ref>
+ </template>
</div>
</div>
</template>
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 9124595..1c6fc0f 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -506,7 +506,8 @@
final ObservableMap<String,Isolate> _isolateCache =
new ObservableMap<String,Isolate>();
- @reflectable Iterable<Isolate> get isolates => _isolateCache.values;
+ // The list of live isolates, ordered by isolate start time.
+ final ObservableList<Isolate> isolates = new ObservableList<Isolate>();
@observable String version = 'unknown';
@observable String targetCPU;
@@ -547,7 +548,8 @@
var isolate = getFromMap(map['isolate']);
event = new ServiceObject._fromMap(isolate, map);
if (event.kind == ServiceEvent.kIsolateExit) {
- _removeIsolate(isolate.id);
+ _isolateCache.remove(isolate.id);
+ _buildIsolateList();
}
}
var eventStream = _eventStreams[streamId];
@@ -558,10 +560,27 @@
}
}
- void _removeIsolate(String isolateId) {
- assert(_isolateCache.containsKey(isolateId));
- _isolateCache.remove(isolateId);
- notifyPropertyChange(#isolates, true, false);
+ int _compareIsolates(Isolate a, Isolate b) {
+ var aStart = a.startTime;
+ var bStart = b.startTime;
+ if (aStart == null) {
+ if (bStart == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ if (bStart == null) {
+ return -1;
+ }
+ return aStart.compareTo(bStart);
+ }
+
+ void _buildIsolateList() {
+ var isolateList = _isolateCache.values.toList();
+ isolateList.sort(_compareIsolates);
+ isolates.clear();
+ isolates.addAll(isolateList);
}
void _removeDeadIsolates(List newIsolates) {
@@ -576,8 +595,8 @@
toRemove.add(id);
}
});
- toRemove.forEach((id) => _removeIsolate(id));
- notifyPropertyChange(#isolates, true, false);
+ toRemove.forEach((id) => _isolateCache.remove(id));
+ _buildIsolateList();
}
static final String _isolateIdPrefix = 'isolates/';
@@ -598,7 +617,7 @@
// Add new isolate to the cache.
isolate = new ServiceObject._fromMap(this, map);
_isolateCache[id] = isolate;
- notifyPropertyChange(#isolates, true, false);
+ _buildIsolateList();
// Eagerly load the isolate.
isolate.load().catchError((e, stack) {
@@ -1201,6 +1220,7 @@
if (map['entry'] != null) {
entry = map['entry'];
}
+ var savedStartTime = startTime;
var startTimeInMillis = map['startTime'];
startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeInMillis);
notifyPropertyChange(#upTime, 0, 1);
@@ -1250,6 +1270,9 @@
libraries.clear();
libraries.addAll(map['libraries']);
libraries.sort(ServiceObject.LexicalSortName);
+ if (savedStartTime == null) {
+ vm._buildIsolateList();
+ }
}
Future<TagProfile> updateTagProfile() {
@@ -1568,7 +1591,7 @@
return Future.wait([refreshDartMetrics(), refreshNativeMetrics()]);
}
- String toString() => "Isolate($_id)";
+ String toString() => "Isolate($name)";
}
/// A [ServiceObject] which implements [ObservableMap].
diff --git a/runtime/observatory/tests/service/isolate_lifecycle_test.dart b/runtime/observatory/tests/service/isolate_lifecycle_test.dart
index dbacef8..2160d0c 100644
--- a/runtime/observatory/tests/service/isolate_lifecycle_test.dart
+++ b/runtime/observatory/tests/service/isolate_lifecycle_test.dart
@@ -40,29 +40,21 @@
return paused;
}
-int numRunning(vm) {
- int running = 0;
- for (var isolate in vm.isolates) {
- if (!isolate.paused) {
- running++;
- }
- }
- return running;
-}
-
var tests = [
(VM vm) async {
- // Wait for the testee to start all of the isolates.
- if (vm.isolates.length != spawnCount + 1) {
- await processServiceEvents(vm, VM.kIsolateStream,
- (event, sub, completer) {
+ Completer completer = new Completer();
+ var stream = await vm.getEventStream(VM.kIsolateStream);
+ if (vm.isolates.length < spawnCount + 1) {
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) {
if (event.kind == ServiceEvent.kIsolateStart) {
- if (vm.isolates.length == spawnCount + 1) {
- sub.cancel();
+ if (vm.isolates.length == (spawnCount + 1)) {
+ subscription.cancel();
completer.complete(null);
}
}
});
+ await completer.future;
}
expect(vm.isolates.length, spawnCount + 1);
},
@@ -75,35 +67,40 @@
},
(VM vm) async {
- // Wait for all spawned isolates to hit pause-at-exit.
- if (numPaused(vm) != spawnCount) {
- await processServiceEvents(vm, VM.kDebugStream,
- (event, sub, completer) {
+ Completer completer = new Completer();
+ var stream = await vm.getEventStream(VM.kDebugStream);
+ if (numPaused(vm) < spawnCount) {
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) {
if (event.kind == ServiceEvent.kPauseExit) {
- if (numPaused(vm) == spawnCount) {
- sub.cancel();
+ if (numPaused(vm) == (spawnCount + 1)) {
+ subscription.cancel();
completer.complete(null);
}
}
});
+ await completer.future;
}
- expect(numPaused(vm), spawnCount);
- expect(numRunning(vm), 1);
+ expect(numPaused(vm), spawnCount + 1);
},
(VM vm) async {
var resumedReceived = 0;
- var eventsDone = processServiceEvents(vm, VM.kIsolateStream,
- (event, sub, completer) {
+ Completer completer = new Completer();
+ var stream = await vm.getEventStream(VM.kIsolateStream);
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) {
if (event.kind == ServiceEvent.kIsolateExit) {
resumedReceived++;
- if (resumedReceived == resumeCount) {
- sub.cancel();
+ if (resumedReceived >= resumeCount) {
+ subscription.cancel();
completer.complete(null);
}
}
});
+
+ // Resume a subset of the isolates.
var resumesIssued = 0;
var isolateList = vm.isolates.toList();
for (var isolate in isolateList) {
@@ -118,12 +115,11 @@
break;
}
}
- return eventsDone;
+ await completer.future;
},
(VM vm) async {
- expect(numPaused(vm), spawnCount - resumeCount);
- expect(numRunning(vm), 1);
+ expect(numPaused(vm), spawnCount + 1 - resumeCount);
},
];
diff --git a/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart b/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart
new file mode 100644
index 0000000..012a68f
--- /dev/null
+++ b/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart
@@ -0,0 +1,58 @@
+// 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.
+// VMOptions=--compile_all --error_on_bad_type --error_on_bad_override
+
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+ print('Hello');
+}
+
+var tests = [
+
+(Isolate isolate) async {
+ Completer completer = new Completer();
+ var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) {
+ if (event.kind == ServiceEvent.kPauseStart) {
+ print('Received PauseStart');
+ subscription.cancel();
+ completer.complete();
+ }
+ });
+
+ if (isolate.pauseEvent != null &&
+ isolate.pauseEvent.kind == ServiceEvent.kPauseStart) {
+ // Wait for the isolate to hit PauseStart.
+ subscription.cancel();
+ } else {
+ await completer.future;
+ }
+
+ completer = new Completer();
+ stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ subscription = stream.listen((ServiceEvent event) {
+ if (event.kind == ServiceEvent.kPauseExit) {
+ print('Received PauseExit');
+ subscription.cancel();
+ completer.complete();
+ }
+ });
+
+ print('Resuming...');
+ isolate.resume();
+
+ // Wait for the isolate to hit PauseExit.
+ await completer.future;
+},
+
+];
+
+main(args) => runIsolateTests(args, tests,
+ testeeConcurrent: testMain,
+ pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory/tests/service/pause_on_start_then_step_test.dart b/runtime/observatory/tests/service/pause_on_start_then_step_test.dart
new file mode 100644
index 0000000..2c7b3b6
--- /dev/null
+++ b/runtime/observatory/tests/service/pause_on_start_then_step_test.dart
@@ -0,0 +1,58 @@
+// 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.
+// VMOptions=--compile_all --error_on_bad_type --error_on_bad_override
+
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+ print('Hello');
+}
+
+var tests = [
+
+(Isolate isolate) async {
+ Completer completer = new Completer();
+ var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ var subscription;
+ subscription = stream.listen((ServiceEvent event) {
+ if (event.kind == ServiceEvent.kPauseStart) {
+ print('Received PauseStart');
+ subscription.cancel();
+ completer.complete();
+ }
+ });
+
+ if (isolate.pauseEvent != null &&
+ isolate.pauseEvent.kind == ServiceEvent.kPauseStart) {
+ // Wait for the isolate to hit PauseStart.
+ subscription.cancel();
+ } else {
+ await completer.future;
+ }
+
+ completer = new Completer();
+ stream = await isolate.vm.getEventStream(VM.kDebugStream);
+ subscription = stream.listen((ServiceEvent event) {
+ if (event.kind == ServiceEvent.kPauseBreakpoint) {
+ print('Received PauseBreakpoint');
+ subscription.cancel();
+ completer.complete();
+ }
+ });
+
+ print('Stepping...');
+ isolate.stepInto();
+
+ // Wait for the isolate to hit PauseBreakpoint.
+ await completer.future;
+},
+
+];
+
+main(args) => runIsolateTests(args, tests,
+ testeeConcurrent: testMain,
+ pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory/tests/service/test_helper.dart b/runtime/observatory/tests/service/test_helper.dart
index 4c492ed..d640dc5 100644
--- a/runtime/observatory/tests/service/test_helper.dart
+++ b/runtime/observatory/tests/service/test_helper.dart
@@ -26,9 +26,12 @@
Platform.script.toFilePath(),
_TESTEE_MODE_FLAG] {}
- Future<int> launch(bool pause_on_exit) {
+ Future<int> launch(bool pause_on_start, bool pause_on_exit) {
String dartExecutable = Platform.executable;
var fullArgs = [];
+ if (pause_on_start == true) {
+ fullArgs.add('--pause-isolates-on-start');
+ }
if (pause_on_exit == true) {
fullArgs.add('--pause-isolates-on-exit');
}
@@ -49,7 +52,7 @@
var port = portExp.firstMatch(line).group(1);
portNumber = int.parse(port);
}
- if (line == '') {
+ if (pause_on_start || line == '') {
// Received blank line.
blank = true;
}
@@ -98,20 +101,26 @@
List<IsolateTest> tests,
{void testeeBefore(),
void testeeConcurrent(),
- bool pause_on_exit}) {
+ bool pause_on_start: false,
+ bool pause_on_exit: false}) {
+ assert(!pause_on_start || testeeBefore == null);
if (mainArgs.contains(_TESTEE_MODE_FLAG)) {
- if (testeeBefore != null) {
- testeeBefore();
+ if (!pause_on_start) {
+ if (testeeBefore != null) {
+ testeeBefore();
+ }
+ print(''); // Print blank line to signal that we are ready.
}
- print(''); // Print blank line to signal that we are ready.
if (testeeConcurrent != null) {
testeeConcurrent();
}
- // Wait around for the process to be killed.
- stdin.first.then((_) => exit(0));
+ if (!pause_on_exit) {
+ // Wait around for the process to be killed.
+ stdin.first.then((_) => exit(0));
+ }
} else {
var process = new _TestLauncher();
- process.launch(pause_on_exit).then((port) {
+ process.launch(pause_on_start, pause_on_exit).then((port) {
if (mainArgs.contains("--gdb")) {
port = 8181;
}
@@ -140,26 +149,6 @@
}
-// Cancel the subscription and complete the completer when finished processing
-// events.
-typedef void ServiceEventHandler(ServiceEvent event,
- StreamSubscription subscription,
- Completer completer);
-
-Future processServiceEvents(VM vm,
- String streamId,
- ServiceEventHandler handler) {
- Completer completer = new Completer();
- vm.getEventStream(streamId).then((stream) {
- var subscription;
- subscription = stream.listen((ServiceEvent event) {
- handler(event, subscription, completer);
- });
- });
- return completer.future;
-}
-
-
Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) {
// Set up a listener to wait for breakpoint events.
Completer completer = new Completer();
@@ -286,20 +275,25 @@
List<VMTest> tests,
{Future testeeBefore(),
Future testeeConcurrent(),
- bool pause_on_exit}) async {
+ bool pause_on_start: false,
+ bool pause_on_exit: false}) async {
if (mainArgs.contains(_TESTEE_MODE_FLAG)) {
- if (testeeBefore != null) {
- await testeeBefore();
+ if (!pause_on_start) {
+ if (testeeBefore != null) {
+ await testeeBefore();
+ }
+ print(''); // Print blank line to signal that we are ready.
}
- print(''); // Print blank line to signal that we are ready.
if (testeeConcurrent != null) {
await testeeConcurrent();
}
- // Wait around for the process to be killed.
- stdin.first.then((_) => exit(0));
+ if (!pause_on_exit) {
+ // Wait around for the process to be killed.
+ stdin.first.then((_) => exit(0));
+ }
} else {
var process = new _TestLauncher();
- process.launch(pause_on_exit).then((port) async {
+ process.launch(pause_on_start, pause_on_exit).then((port) async {
if (mainArgs.contains("--gdb")) {
port = 8181;
}
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 038bfc8..3ccb68b 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1568,6 +1568,11 @@
} else {
lib ^= Api::UnwrapHandle(library);
}
+ isolate->heap()->CollectAllGarbage();
+#if defined(DEBUG)
+ FunctionVisitor check_canonical(isolate);
+ isolate->heap()->IterateObjects(&check_canonical);
+#endif // #if defined(DEBUG).
ScriptSnapshotWriter writer(buffer, ApiReallocate);
writer.WriteScriptSnapshot(lib);
*size = writer.BytesWritten();
diff --git a/runtime/vm/dart_api_message.cc b/runtime/vm/dart_api_message.cc
index aa08099..69fb891 100644
--- a/runtime/vm/dart_api_message.cc
+++ b/runtime/vm/dart_api_message.cc
@@ -535,6 +535,7 @@
intptr_t object_id) {
switch (class_id) {
case kClassCid: {
+ Read<bool>(); // Consume the is_in_fullsnapshot indicator.
Dart_CObject_Internal* object = AllocateDartCObjectClass();
AddBackRef(object_id, object, kIsDeserialized);
object->internal.as_class.library_url = ReadObjectImpl();
@@ -768,10 +769,21 @@
READ_TYPED_DATA(Float64, double);
case kGrowableObjectArrayCid: {
- // A GrowableObjectArray is serialized as its length followed by
- // its backing store. The backing store is an array with a
- // length which might be longer than the length of the
- // GrowableObjectArray.
+ // A GrowableObjectArray is serialized as its type arguments and
+ // length followed by its backing store. The backing store is an
+ // array with a length which might be longer than the length of
+ // the GrowableObjectArray.
+
+ // Read and skip the type arguments field.
+ // TODO(sjesse): Remove this when message serialization format is
+ // updated (currently type_arguments is leaked).
+ Dart_CObject* type_arguments = ReadObjectImpl();
+ if (type_arguments != &type_arguments_marker &&
+ type_arguments->type != Dart_CObject_kNull) {
+ return AllocateDartCObjectUnsupported();
+ }
+
+ // Read the length field.
intptr_t len = ReadSmiValue();
Dart_CObject* value = GetBackRef(object_id);
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index c7d549c..5573f4b 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1270,6 +1270,7 @@
resume_action_ = kStepOut;
}
+
RawFunction* Debugger::ResolveFunction(const Library& library,
const String& class_name,
const String& function_name) {
@@ -2322,6 +2323,13 @@
}
+void Debugger::EnterSingleStepMode() {
+ stepping_fp_ = 0;
+ DeoptimizeWorld();
+ isolate_->set_single_step(true);
+}
+
+
void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace) {
stepping_fp_ = 0;
if (resume_action_ == kSingleStep) {
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index faaf558..79a620c 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -459,6 +459,12 @@
bool IsPaused() const { return pause_event_ != NULL; }
+ // Put the isolate into single stepping mode when Dart code next runs.
+ //
+ // This is used by the vm service to allow the user to step while
+ // paused at isolate start.
+ void EnterSingleStepMode();
+
// Indicates why the debugger is currently paused. If the debugger
// is not paused, this returns NULL. Note that the debugger can be
// paused for breakpoints, isolate interruption, and (sometimes)
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index a433948..a646017 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -1818,6 +1818,16 @@
}
+// Use function name to determine if inlineable operator.
+// TODO(srdjan): add names as necessary
+static bool IsInlineableOperator(const Function& function) {
+ return (function.name() == Symbols::IndexToken().raw()) ||
+ (function.name() == Symbols::AssignIndexToken().raw()) ||
+ (function.name() == Symbols::Plus().raw()) ||
+ (function.name() == Symbols::Minus().raw());
+}
+
+
bool FlowGraphInliner::AlwaysInline(const Function& function) {
const char* kAlwaysInlineAnnotation = "AlwaysInline";
if (FLAG_enable_inlining_annotations &&
@@ -1828,7 +1838,8 @@
}
if (function.IsImplicitGetterFunction() || function.IsGetterFunction() ||
- function.IsImplicitSetterFunction() || function.IsSetterFunction()) {
+ function.IsImplicitSetterFunction() || function.IsSetterFunction() ||
+ IsInlineableOperator(function)) {
const intptr_t count = function.optimized_instruction_count();
if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) {
return true;
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index f2572a1..cf07132 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -51,6 +51,7 @@
DECLARE_FLAG(bool, polymorphic_with_deopt);
DECLARE_FLAG(bool, source_lines);
+DECLARE_FLAG(bool, trace_field_guards);
DECLARE_FLAG(bool, trace_type_check_elimination);
DECLARE_FLAG(bool, warn_on_javascript_compatibility);
@@ -4596,14 +4597,25 @@
Function::Handle(Z, owner.LookupGetterFunction(field_name));
const Function& setter =
Function::Handle(Z, owner.LookupSetterFunction(field_name));
- bool result = !getter.IsNull()
- && !setter.IsNull()
- && (setter.usage_counter() > 0)
- && (FLAG_getter_setter_ratio * setter.usage_counter() >=
- getter.usage_counter());
- if (!result) {
- if (FLAG_trace_optimization) {
+ bool unboxed_field = false;
+ if (!getter.IsNull() && !setter.IsNull()) {
+ if (field.is_double_initialized()) {
+ unboxed_field = true;
+ } else if ((setter.usage_counter() > 0) &&
+ ((FLAG_getter_setter_ratio * setter.usage_counter()) >=
+ getter.usage_counter())) {
+ unboxed_field = true;
+ }
+ }
+ if (!unboxed_field) {
+ if (FLAG_trace_optimization || FLAG_trace_field_guards) {
ISL_Print("Disabling unboxing of %s\n", field.ToCString());
+ if (!setter.IsNull()) {
+ OS::Print(" setter usage count: %" Pd "\n", setter.usage_counter());
+ }
+ if (!getter.IsNull()) {
+ OS::Print(" getter usage count: %" Pd "\n", getter.usage_counter());
+ }
}
field.set_is_unboxing_candidate(false);
field.DeoptimizeDependentCode();
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 44919ff..9bae137 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -335,6 +335,8 @@
void PrintTo(BufferFormatter* f) const;
+ const char* ToCString() const;
+
const char* DebugName() const { return "Value"; }
bool IsSmiValue() { return Type()->ToCid() == kSmiCid; }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 83e45e9..4351b1d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -399,7 +399,7 @@
uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld);
null_ = reinterpret_cast<RawInstance*>(address + kHeapObjectTag);
// The call below is using 'null_' to initialize itself.
- InitializeObject(address, kNullCid, Instance::InstanceSize());
+ InitializeObject(address, kNullCid, Instance::InstanceSize(), true);
}
}
@@ -462,7 +462,7 @@
intptr_t size = Class::InstanceSize();
uword address = heap->Allocate(size, Heap::kOld);
class_class_ = reinterpret_cast<RawClass*>(address + kHeapObjectTag);
- InitializeObject(address, Class::kClassId, size);
+ InitializeObject(address, Class::kClassId, size, true);
Class fake;
// Initialization from Class::New<Class>.
@@ -635,7 +635,7 @@
// Allocate and initialize the empty_array instance.
{
uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld);
- InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0));
+ InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0), true);
Array::initializeHandle(
empty_array_,
reinterpret_cast<RawArray*>(address + kHeapObjectTag));
@@ -646,7 +646,7 @@
// Allocate and initialize the zero_array instance.
{
uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld);
- InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1));
+ InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1), true);
Array::initializeHandle(
zero_array_,
reinterpret_cast<RawArray*>(address + kHeapObjectTag));
@@ -661,7 +661,8 @@
heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld);
InitializeObject(address,
kObjectPoolCid,
- ObjectPool::InstanceSize(0));
+ ObjectPool::InstanceSize(0),
+ true);
ObjectPool::initializeHandle(
empty_object_pool_,
reinterpret_cast<RawObjectPool*>(address + kHeapObjectTag));
@@ -673,7 +674,8 @@
{
uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld);
InitializeObject(address, kPcDescriptorsCid,
- PcDescriptors::InstanceSize(0));
+ PcDescriptors::InstanceSize(0),
+ true);
PcDescriptors::initializeHandle(
empty_descriptors_,
reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag));
@@ -687,7 +689,8 @@
heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld);
InitializeObject(address,
kLocalVarDescriptorsCid,
- LocalVarDescriptors::InstanceSize(0));
+ LocalVarDescriptors::InstanceSize(0),
+ true);
LocalVarDescriptors::initializeHandle(
empty_var_descriptors_,
reinterpret_cast<RawLocalVarDescriptors*>(address + kHeapObjectTag));
@@ -703,7 +706,8 @@
heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld);
InitializeObject(address,
kExceptionHandlersCid,
- ExceptionHandlers::InstanceSize(0));
+ ExceptionHandlers::InstanceSize(0),
+ true);
ExceptionHandlers::initializeHandle(
empty_exception_handlers_,
reinterpret_cast<RawExceptionHandlers*>(address + kHeapObjectTag));
@@ -809,6 +813,7 @@
ASSERT(!obj->IsMarked());
// Free list elements should never be marked.
if (!obj->IsFreeListElement()) {
+ ASSERT(obj->IsVMHeapObject());
obj->SetMarkBitUnsynchronized();
}
}
@@ -1473,7 +1478,7 @@
#define ADD_SET_FIELD(clazz) \
field_name = Symbols::New("cid"#clazz); \
- field = Field::New(field_name, true, false, true, true, cls, 0); \
+ field = Field::New(field_name, true, false, true, false, cls, 0); \
value = Smi::New(k##clazz##Cid); \
field.set_value(value); \
field.set_type(Type::Handle(Type::IntType())); \
@@ -1674,7 +1679,10 @@
}
-void Object::InitializeObject(uword address, intptr_t class_id, intptr_t size) {
+void Object::InitializeObject(uword address,
+ intptr_t class_id,
+ intptr_t size,
+ bool is_vm_object) {
// TODO(iposva): Get a proper halt instruction from the assembler which
// would be needed here for code objects.
uword initial_value = reinterpret_cast<uword>(null_);
@@ -1688,7 +1696,9 @@
ASSERT(class_id != kIllegalCid);
tags = RawObject::ClassIdTag::update(class_id, tags);
tags = RawObject::SizeTag::update(size, tags);
+ tags = RawObject::VMHeapObjectTag::update(is_vm_object, tags);
reinterpret_cast<RawObject*>(address)->tags_ = tags;
+ ASSERT(is_vm_object == RawObject::IsVMHeapObject(tags));
VerifiedMemory::Accept(address, size);
}
@@ -1746,7 +1756,7 @@
Profiler::RecordAllocation(isolate, cls_id);
}
NoSafepointScope no_safepoint;
- InitializeObject(address, cls_id, size);
+ InitializeObject(address, cls_id, size, (isolate == Dart::vm_isolate()));
RawObject* raw_obj = reinterpret_cast<RawObject*>(address + kHeapObjectTag);
ASSERT(cls_id == RawObject::ClassIdTag::decode(raw_obj->ptr()->tags_));
return raw_obj;
@@ -1840,6 +1850,12 @@
}
+bool Class::IsInFullSnapshot() const {
+ NoSafepointScope no_safepoint;
+ return raw_ptr()->library_->ptr()->is_in_fullsnapshot_;
+}
+
+
RawType* Class::SignatureType() const {
ASSERT(IsSignatureClass());
const Function& function = Function::Handle(signature_function());
@@ -7214,7 +7230,7 @@
bool is_static,
bool is_final,
bool is_const,
- bool is_synthetic,
+ bool is_reflectable,
const Class& owner,
intptr_t token_pos) {
ASSERT(!owner.IsNull());
@@ -7228,7 +7244,8 @@
}
result.set_is_final(is_final);
result.set_is_const(is_const);
- result.set_is_synthetic(is_synthetic);
+ result.set_is_reflectable(is_reflectable);
+ result.set_is_double_initialized(false);
result.set_owner(owner);
result.set_token_pos(token_pos);
result.set_has_initializer(false);
@@ -7443,7 +7460,7 @@
true, // is_static
true, // is_final
true, // is_const
- true, // is_synthetic
+ false, // is_reflectable
field_owner,
this->token_pos());
closure_field.set_value(Instance::Cast(result));
@@ -9032,7 +9049,7 @@
true, // is_static
false, // is_final
false, // is_const
- true, // is_synthetic
+ false, // is_reflectable
cls,
token_pos));
field.set_type(Type::Handle(Type::DynamicType()));
@@ -9825,6 +9842,7 @@
result.StorePointer(&result.raw_ptr()->load_error_, Instance::null());
result.set_native_entry_resolver(NULL);
result.set_native_entry_symbol_resolver(NULL);
+ result.set_is_in_fullsnapshot(false);
result.StoreNonPointer(&result.raw_ptr()->corelib_imported_, true);
result.set_debuggable(false);
result.set_is_dart_scheme(url.StartsWith(Symbols::DartScheme()));
@@ -10540,7 +10558,7 @@
true, // is_static
false, // is_final
false, // is_const
- true, // is_synthetic
+ false, // is_reflectable
owner_class,
token_pos));
field.set_type(Type::Handle(Type::DynamicType()));
@@ -15257,6 +15275,7 @@
void Type::SetIsFinalized() const {
ASSERT(!IsFinalized());
if (IsInstantiated()) {
+ ASSERT(HasResolvedTypeClass());
set_type_state(RawType::kFinalizedInstantiated);
} else {
set_type_state(RawType::kFinalizedUninstantiated);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5bfc041..8d3ef5e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -249,23 +249,6 @@
return AtomicOperations::CompareAndSwapWord(
&raw()->ptr()->tags_, old_tags, new_tags);
}
- void set_tags(intptr_t value) const {
- ASSERT(!IsNull());
- // TODO(asiva): Remove the capability of setting tags in general. The mask
- // here only allows for canonical and from_snapshot flags to be set.
- value = value & 0x0000000c;
- uword tags = raw()->ptr()->tags_;
- uword old_tags;
- do {
- old_tags = tags;
- uword new_tags = (old_tags & ~0x0000000c) | value;
- tags = CompareAndSwapTags(old_tags, new_tags);
- } while (tags != old_tags);
- }
- void SetCreatedFromSnapshot() const {
- ASSERT(!IsNull());
- raw()->SetCreatedFromSnapshot();
- }
bool IsCanonical() const {
ASSERT(!IsNull());
return raw()->IsCanonical();
@@ -728,7 +711,10 @@
return -kWordSize;
}
- static void InitializeObject(uword address, intptr_t id, intptr_t size);
+ static void InitializeObject(uword address,
+ intptr_t id,
+ intptr_t size,
+ bool is_vm_object);
static void RegisterClass(const Class& cls,
const String& name,
@@ -945,6 +931,7 @@
RawString* Name() const;
RawString* PrettyName() const;
RawString* UserVisibleName() const;
+ bool IsInFullSnapshot() const;
virtual RawString* DictionaryName() const { return Name(); }
@@ -1115,6 +1102,10 @@
static bool IsSignatureClass(RawClass* cls) {
return cls->ptr()->signature_function_ != Object::null();
}
+ static bool IsInFullSnapshot(RawClass* cls) {
+ NoSafepointScope no_safepoint;
+ return cls->ptr()->library_->ptr()->is_in_fullsnapshot_;
+ }
// Check if this class represents a canonical signature class, i.e. not an
// alias as defined in a typedef.
@@ -2814,8 +2805,14 @@
bool is_static() const { return StaticBit::decode(raw_ptr()->kind_bits_); }
bool is_final() const { return FinalBit::decode(raw_ptr()->kind_bits_); }
bool is_const() const { return ConstBit::decode(raw_ptr()->kind_bits_); }
- bool is_synthetic() const {
- return SyntheticBit::decode(raw_ptr()->kind_bits_);
+ bool is_reflectable() const {
+ return ReflectableBit::decode(raw_ptr()->kind_bits_);
+ }
+ bool is_double_initialized() const {
+ return DoubleInitializedBit::decode(raw_ptr()->kind_bits_);
+ }
+ void set_is_double_initialized(bool value) const {
+ set_kind_bits(DoubleInitializedBit::update(value, raw_ptr()->kind_bits_));
}
inline intptr_t Offset() const;
@@ -2838,7 +2835,7 @@
bool is_static,
bool is_final,
bool is_const,
- bool is_synthetic,
+ bool is_reflectable,
const Class& owner,
intptr_t token_pos);
@@ -2987,16 +2984,18 @@
kFinalBit,
kHasInitializerBit,
kUnboxingCandidateBit,
- kSyntheticBit
+ kReflectableBit,
+ kDoubleInitializedBit,
};
class ConstBit : public BitField<bool, kConstBit, 1> {};
class StaticBit : public BitField<bool, kStaticBit, 1> {};
class FinalBit : public BitField<bool, kFinalBit, 1> {};
class HasInitializerBit : public BitField<bool, kHasInitializerBit, 1> {};
class UnboxingCandidateBit : public BitField<bool,
- kUnboxingCandidateBit, 1> {
- };
- class SyntheticBit : public BitField<bool, kSyntheticBit, 1> {};
+ kUnboxingCandidateBit, 1> {};
+ class ReflectableBit : public BitField<bool, kReflectableBit, 1> {};
+ class DoubleInitializedBit : public BitField<bool,
+ kDoubleInitializedBit, 1> {};
// Update guarded cid and guarded length for this field. Returns true, if
// deoptimization of dependent code is required.
@@ -3012,8 +3011,8 @@
void set_is_const(bool value) const {
set_kind_bits(ConstBit::update(value, raw_ptr()->kind_bits_));
}
- void set_is_synthetic(bool value) const {
- set_kind_bits(SyntheticBit::update(value, raw_ptr()->kind_bits_));
+ void set_is_reflectable(bool value) const {
+ set_kind_bits(ReflectableBit::update(value, raw_ptr()->kind_bits_));
}
void set_owner(const Object& value) const {
StorePointer(&raw_ptr()->owner_, value.raw());
@@ -3384,6 +3383,11 @@
native_symbol_resolver);
}
+ bool is_in_fullsnapshot() const { return raw_ptr()->is_in_fullsnapshot_; }
+ void set_is_in_fullsnapshot(bool value) const {
+ StoreNonPointer(&raw_ptr()->is_in_fullsnapshot_, value);
+ }
+
RawError* Patch(const Script& script) const;
RawString* PrivateName(const String& name) const;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 156fb48..764de50 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -281,7 +281,7 @@
const Array& one_fields = Array::Handle(Array::New(1));
const String& field_name = String::Handle(Symbols::New("the_field"));
const Field& field = Field::Handle(
- Field::New(field_name, false, false, false, false, one_field_class, 0));
+ Field::New(field_name, false, false, false, true, one_field_class, 0));
one_fields.SetAt(0, field);
one_field_class.SetFields(one_fields);
one_field_class.Finalize();
@@ -2991,7 +2991,7 @@
const Class& cls = Class::Handle(CreateTestClass("global:"));
const String& field_name = String::Handle(Symbols::New(name));
const Field& field =
- Field::Handle(Field::New(field_name, true, false, false, false, cls, 0));
+ Field::Handle(Field::New(field_name, true, false, false, true, cls, 0));
return field.raw();
}
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 0179633..419cd4e 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -3972,7 +3972,8 @@
// value is not assignable (assuming checked mode and disregarding actual
// mode), the field value is reset and a kImplicitStaticFinalGetter is
// created at finalization time.
- if (LookaheadToken(1) == Token::kSEMICOLON) {
+ if ((LookaheadToken(1) == Token::kSEMICOLON) ||
+ (LookaheadToken(1) == Token::kCOMMA)) {
has_simple_literal = IsSimpleLiteral(*field->type, &init_value);
}
SkipExpr();
@@ -3988,11 +3989,13 @@
}
// Create the field object.
+ const bool is_reflectable =
+ !(library_.is_dart_scheme() && library_.IsPrivate(*field->name));
class_field = Field::New(*field->name,
field->has_static,
field->has_final,
field->has_const,
- false, // Not synthetic.
+ is_reflectable,
current_class(),
field->name_pos);
class_field.set_type(*field->type);
@@ -4008,6 +4011,9 @@
// and rules out many fields from being unnecessary unboxing candidates.
if (!field->has_static && has_initializer && has_simple_literal) {
class_field.RecordStore(init_value);
+ if (!init_value.IsNull() && init_value.IsDouble()) {
+ class_field.set_is_double_initialized(true);
+ }
}
// For static final fields (this includes static const fields), set value to
@@ -4671,7 +4677,7 @@
false, // Not static.
true, // Field is final.
false, // Not const.
- false, // Not synthetic.
+ true, // Is reflectable.
cls,
cls.token_pos());
index_field.set_type(int_type);
@@ -4743,7 +4749,7 @@
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
- /* is_synthetic = */ false,
+ /* is_reflectable = */ true,
cls,
cls.token_pos());
enum_value.set_type(dynamic_type);
@@ -4781,7 +4787,7 @@
/* is_static = */ true,
/* is_final = */ true,
/* is_const = */ true,
- /* is_synthetic = */ false,
+ /* is_reflectable = */ true,
cls,
cls.token_pos());
values_field.set_type(Type::Handle(Z, Type::ArrayType()));
@@ -5365,7 +5371,6 @@
// Const fields are implicitly final.
const bool is_final = is_const || (CurrentToken() == Token::kFINAL);
const bool is_static = true;
- const bool is_synthetic = false;
const AbstractType& type = AbstractType::ZoneHandle(Z,
ParseConstFinalVarOrType(ClassFinalizer::kResolveTypeParameters));
Field& field = Field::Handle(Z);
@@ -5393,7 +5398,9 @@
var_name.ToCString());
}
- field = Field::New(var_name, is_static, is_final, is_const, is_synthetic,
+ const bool is_reflectable =
+ !(library_.is_dart_scheme() && library_.IsPrivate(var_name));
+ field = Field::New(var_name, is_static, is_final, is_const, is_reflectable,
current_class(), name_pos);
field.set_type(type);
field.set_value(Instance::Handle(Z, Instance::null()));
@@ -11184,18 +11191,6 @@
ClassFinalizer::kCanonicalize);
ASSERT(!type_parameter.IsMalformed());
left = new(Z) TypeNode(primary->token_pos(), type_parameter);
- } else if (is_conditional && primary_node->primary().IsClass()) {
- // The left-hand side of ?. is interpreted as an expression
- // of type Type, not as a class literal.
- const Class& type_class = Class::Cast(primary_node->primary());
- AbstractType& type = Type::ZoneHandle(Z,
- Type::New(type_class, TypeArguments::Handle(Z),
- primary_pos, Heap::kOld));
- type ^= ClassFinalizer::FinalizeType(
- current_class(), type, ClassFinalizer::kCanonicalize);
- // Type may be malbounded, but not malformed.
- ASSERT(!type.IsMalformed());
- left = new(Z) TypeNode(primary_pos, type);
} else {
// Super field access handled in ParseSuperFieldAccess(),
// super calls handled in ParseSuperCall().
@@ -11210,7 +11205,6 @@
if (left->IsPrimaryNode() &&
left->AsPrimaryNode()->primary().IsClass()) {
// Static method call prefixed with class name.
- ASSERT(!is_conditional);
const Class& cls = Class::Cast(left->AsPrimaryNode()->primary());
selector = ParseStaticCall(cls, *ident, ident_pos);
} else {
@@ -11237,7 +11231,6 @@
is_conditional);
} else {
// Static field access.
- ASSERT(!is_conditional);
selector = GenerateStaticFieldAccess(cls, *ident, ident_pos);
ASSERT(selector != NULL);
if (selector->IsLoadStaticFieldNode()) {
@@ -12865,9 +12858,17 @@
RawFunction* Parser::BuildConstructorClosureFunction(const Function& ctr,
intptr_t token_pos) {
ASSERT(ctr.kind() == RawFunction::kConstructor);
+ Function& closure = Function::Handle(Z);
+ closure = current_class().LookupClosureFunction(token_pos);
+ if (!closure.IsNull()) {
+ ASSERT(closure.IsConstructorClosureFunction());
+ return closure.raw();
+ }
+
String& closure_name = String::Handle(Z, ctr.name());
closure_name = Symbols::FromConcat(Symbols::ConstructorClosurePrefix(),
closure_name);
+
ParamList params;
params.AddFinalParameter(token_pos,
&Symbols::ClosureParameter(),
@@ -12878,11 +12879,12 @@
// Replace the types parsed from the constructor.
params.EraseParameterTypes();
- Function& closure = Function::Handle(Z);
closure = Function::NewClosureFunction(closure_name,
innermost_function(),
token_pos);
closure.set_is_generated_body(true);
+ closure.set_is_debuggable(false);
+ closure.set_is_visible(false);
closure.set_result_type(AbstractType::Handle(Type::DynamicType()));
AddFormalParamsToFunction(¶ms, closure);
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 67f0afc..61c2994 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -18,10 +18,6 @@
DEFINE_FLAG(bool, validate_overwrite, true, "Verify overwritten fields.");
#endif // DEBUG
-bool RawObject::IsVMHeapObject() const {
- return Dart::vm_isolate()->heap()->Contains(ToAddr(this));
-}
-
void RawObject::Validate(Isolate* isolate) const {
if (Object::void_class_ == reinterpret_cast<RawClass*>(kHeapObjectTag)) {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index fa91a14..517379b 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -236,7 +236,7 @@
kWatchedBit = 0,
kMarkBit = 1,
kCanonicalBit = 2,
- kFromSnapshotBit = 3,
+ kVMHeapObjectBit = 3,
kRememberedBit = 4,
#if defined(ARCH_IS_32_BIT)
kReservedTagPos = 5, // kReservedBit{100K,1M,10M}
@@ -315,8 +315,6 @@
uword addr = reinterpret_cast<uword>(this);
return (addr & kNewObjectAlignmentOffset) == kOldObjectAlignmentOffset;
}
- // Assumes this is a heap object.
- bool IsVMHeapObject() const;
// Like !IsHeapObject() || IsOldObject(), but compiles to a single branch.
bool IsSmiOrOldObject() const {
@@ -372,11 +370,11 @@
void ClearCanonical() {
UpdateTagBit<CanonicalObjectTag>(false);
}
- bool IsCreatedFromSnapshot() const {
- return CreatedFromSnapshotTag::decode(ptr()->tags_);
+ bool IsVMHeapObject() const {
+ return VMHeapObjectTag::decode(ptr()->tags_);
}
- void SetCreatedFromSnapshot() {
- UpdateTagBit<CreatedFromSnapshotTag>(true);
+ void SetVMHeapObject() {
+ UpdateTagBit<VMHeapObjectTag>(true);
}
// Support for GC remembered bit.
@@ -445,8 +443,8 @@
return reinterpret_cast<uword>(raw_obj->ptr());
}
- static bool IsCreatedFromSnapshot(intptr_t value) {
- return CreatedFromSnapshotTag::decode(value);
+ static bool IsVMHeapObject(intptr_t value) {
+ return VMHeapObjectTag::decode(value);
}
static bool IsCanonical(intptr_t value) {
@@ -482,7 +480,7 @@
class CanonicalObjectTag : public BitField<bool, kCanonicalBit, 1> {};
- class CreatedFromSnapshotTag : public BitField<bool, kFromSnapshotBit, 1> {};
+ class VMHeapObjectTag : public BitField<bool, kVMHeapObjectBit, 1> {};
class ReservedBits : public
BitField<intptr_t, kReservedTagPos, kReservedTagSize> {}; // NOLINT
@@ -847,7 +845,7 @@
// generated on platforms with weak addressing modes (ARM, MIPS).
int8_t guarded_list_length_in_object_offset_;
- uint8_t kind_bits_; // static, final, const, has initializer.
+ uint8_t kind_bits_; // static, final, const, has initializer....
};
@@ -950,7 +948,9 @@
bool corelib_imported_;
bool is_dart_scheme_;
bool debuggable_; // True if debugger can stop in library.
+ bool is_in_fullsnapshot_; // True if library is in a full snapshot.
+ friend class Class;
friend class Isolate;
};
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index cc193fa..9a12d08 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -31,8 +31,9 @@
ASSERT(reader != NULL);
Class& cls = Class::ZoneHandle(reader->zone(), Class::null());
+ bool is_in_fullsnapshot = reader->Read<bool>();
if ((kind == Snapshot::kFull) ||
- (kind == Snapshot::kScript && !RawObject::IsCreatedFromSnapshot(tags))) {
+ (kind == Snapshot::kScript && !is_in_fullsnapshot)) {
// Read in the base information.
classid_t class_id = reader->ReadClassIDValue();
@@ -49,9 +50,6 @@
}
reader->AddBackRef(object_id, &cls, kIsDeserialized);
- // Set the object tags.
- cls.set_tags(tags);
-
// Set all non object fields.
if (!RawObject::IsInternalVMdefinedClassId(class_id)) {
// Instance size of a VM defined class is already set up.
@@ -76,8 +74,10 @@
cls.StorePointer((cls.raw()->from() + i),
reader->PassiveObjectHandle()->raw());
}
+ ASSERT(!cls.IsInFullSnapshot() || (kind == Snapshot::kFull));
} else {
cls ^= reader->ReadClassId(object_id);
+ ASSERT((kind == Snapshot::kMessage) || cls.IsInFullSnapshot());
}
return cls.raw();
}
@@ -87,17 +87,22 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
+ bool is_in_fullsnapshot = Class::IsInFullSnapshot(this);
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
- if ((kind == Snapshot::kFull) ||
- (kind == Snapshot::kScript &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this)))) {
- // Write out the class and tags information.
- writer->WriteVMIsolateObject(kClassCid);
- writer->WriteTags(writer->GetObjectTags(this));
+ // Write out the class and tags information.
+ writer->WriteVMIsolateObject(kClassCid);
+ writer->WriteTags(writer->GetObjectTags(this));
+ // Write out the boolean is_in_fullsnapshot first as this will
+ // help the reader decide how the rest of the information needs
+ // to be interpreted.
+ writer->Write<bool>(is_in_fullsnapshot);
+
+ if ((kind == Snapshot::kFull) ||
+ (kind == Snapshot::kScript && !is_in_fullsnapshot)) {
// Write out all the non object pointer fields.
// NOTE: cpp_vtable_ is not written.
classid_t class_id = ptr()->id_;
@@ -144,9 +149,6 @@
reader->zone(), NEW_OBJECT(UnresolvedClass));
reader->AddBackRef(object_id, &unresolved_class, kIsDeserialized);
- // Set the object tags.
- unresolved_class.set_tags(tags);
-
// Set all non object fields.
unresolved_class.set_token_pos(reader->Read<int32_t>());
@@ -210,9 +212,7 @@
// 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::kScript && RawObject::IsCreatedFromSnapshot(tags)) ||
- kind == Snapshot::kMessage);
+ bool defer_canonicalization = is_canonical && (kind != Snapshot::kFull);
reader->AddBackRef(object_id, &type, kIsDeserialized, defer_canonicalization);
// Set all non object fields.
@@ -226,13 +226,15 @@
intptr_t from_offset = OFFSET_OF_FROM(type);
for (intptr_t i = 0; i <= num_flds; i++) {
(*reader->PassiveObjectHandle()) =
- reader->ReadObjectImpl(kAsInlinedObject, object_id, (i + from_offset));
+ reader->ReadObjectImpl(kAsReference, object_id, (i + from_offset));
type.StorePointer((type.raw()->from() + i),
reader->PassiveObjectHandle()->raw());
}
- // Set the object tags.
- type.set_tags(tags);
+ // Set the canonical bit.
+ if (!defer_canonicalization && RawObject::IsCanonical(tags)) {
+ type.SetCanonical();
+ }
return type.raw();
}
@@ -246,6 +248,7 @@
// Only resolved and finalized types should be written to a snapshot.
ASSERT((ptr()->type_state_ == RawType::kFinalizedInstantiated) ||
(ptr()->type_state_ == RawType::kFinalizedUninstantiated));
+ ASSERT(ptr()->type_class_ != Object::null());
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -261,7 +264,8 @@
// Write out all the object pointer fields. Since we will be canonicalizing
// the type object when reading it back we should write out all the fields
// inline and not as references.
- SnapshotWriterVisitor visitor(writer, false);
+ ASSERT(ptr()->type_class_ != Object::null());
+ SnapshotWriterVisitor visitor(writer);
visitor.VisitPointers(from(), to());
}
@@ -277,9 +281,6 @@
reader->zone(), NEW_OBJECT(TypeRef));
reader->AddBackRef(object_id, &type_ref, kIsDeserialized);
- // Set the object tags.
- type_ref.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -325,9 +326,6 @@
reader->zone(), NEW_OBJECT(TypeParameter));
reader->AddBackRef(object_id, &type_parameter, kIsDeserialized);
- // Set the object tags.
- type_parameter.set_tags(tags);
-
// Set all non object fields.
type_parameter.set_token_pos(reader->Read<int32_t>());
type_parameter.set_index(reader->Read<int16_t>());
@@ -387,9 +385,6 @@
reader->zone(), NEW_OBJECT(BoundedType));
reader->AddBackRef(object_id, &bounded_type, kIsDeserialized);
- // Set the object tags.
- bounded_type.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -453,9 +448,7 @@
TypeArguments& type_arguments = TypeArguments::ZoneHandle(
reader->zone(), NEW_OBJECT_WITH_LEN_SPACE(TypeArguments, len, kind));
bool is_canonical = RawObject::IsCanonical(tags);
- bool defer_canonicalization = is_canonical &&
- ((kind == Snapshot::kScript && RawObject::IsCreatedFromSnapshot(tags)) ||
- kind == Snapshot::kMessage);
+ bool defer_canonicalization = is_canonical && (kind != Snapshot::kFull);
reader->AddBackRef(object_id,
&type_arguments,
kIsDeserialized,
@@ -474,12 +467,14 @@
reinterpret_cast<RawAbstractType**>(type_arguments.raw()->ptr());
for (intptr_t i = 0; i < len; i++) {
*reader->TypeHandle() ^=
- reader->ReadObjectImpl(kAsInlinedObject, object_id, (i + offset));
+ reader->ReadObjectImpl(kAsReference, object_id, (i + offset));
type_arguments.SetTypeAt(i, *reader->TypeHandle());
}
- // Set the object tags .
- type_arguments.set_tags(tags);
+ // Set the canonical bit.
+ if (!defer_canonicalization && RawObject::IsCanonical(tags)) {
+ type_arguments.SetCanonical();
+ }
return type_arguments.raw();
}
@@ -508,7 +503,7 @@
// Write out the individual types.
intptr_t len = Smi::Value(ptr()->length_);
for (intptr_t i = 0; i < len; i++) {
- writer->WriteObjectImpl(ptr()->types()[i], kAsInlinedObject);
+ writer->WriteObjectImpl(ptr()->types()[i], kAsReference);
}
}
@@ -518,18 +513,12 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
// Allocate function object.
PatchClass& cls = PatchClass::ZoneHandle(reader->zone(),
NEW_OBJECT(PatchClass));
reader->AddBackRef(object_id, &cls, kIsDeserialized);
- // Set the object tags.
- cls.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -539,6 +528,9 @@
cls.StorePointer((cls.raw()->from() + i),
reader->PassiveObjectHandle()->raw());
}
+ ASSERT(((kind == Snapshot::kScript) &&
+ !Class::IsInFullSnapshot(cls.source_class())) ||
+ (kind == Snapshot::kFull));
return cls.raw();
}
@@ -548,9 +540,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -569,18 +559,13 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate closure data object.
ClosureData& data = ClosureData::ZoneHandle(
reader->zone(), NEW_OBJECT(ClosureData));
reader->AddBackRef(object_id, &data, kIsDeserialized);
- // Set the object tags.
- data.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -597,9 +582,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -629,18 +612,13 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate redirection data object.
RedirectionData& data = RedirectionData::ZoneHandle(
reader->zone(), NEW_OBJECT(RedirectionData));
reader->AddBackRef(object_id, &data, kIsDeserialized);
- // Set the object tags.
- data.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -661,9 +639,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -683,18 +659,13 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate function object.
Function& func = Function::ZoneHandle(
reader->zone(), NEW_OBJECT(Function));
reader->AddBackRef(object_id, &func, kIsDeserialized);
- // Set the object tags.
- func.set_tags(tags);
-
// Set all the non object fields.
func.set_token_pos(reader->Read<int32_t>());
func.set_end_token_pos(reader->Read<int32_t>());
@@ -730,9 +701,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -764,17 +733,12 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate field object.
Field& field = Field::ZoneHandle(reader->zone(), NEW_OBJECT(Field));
reader->AddBackRef(object_id, &field, kIsDeserialized);
- // Set the object tags.
- field.set_tags(tags);
-
// Set all non object fields.
field.set_token_pos(reader->Read<int32_t>());
field.set_guarded_cid(reader->Read<int32_t>());
@@ -803,9 +767,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -838,9 +800,6 @@
reader->zone(), NEW_OBJECT(LiteralToken));
reader->AddBackRef(object_id, &literal_token, kIsDeserialized);
- // Set the object tags.
- literal_token.set_tags(tags);
-
// Read the token attributes.
Token::Kind token_kind = static_cast<Token::Kind>(reader->Read<int32_t>());
literal_token.set_kind(token_kind);
@@ -886,9 +845,7 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Read the length so that we can determine number of tokens to read.
intptr_t len = reader->ReadSmiValue();
@@ -898,9 +855,6 @@
reader->zone(), NEW_OBJECT_WITH_LEN(TokenStream, len));
reader->AddBackRef(object_id, &token_stream, kIsDeserialized);
- // Set the object tags.
- token_stream.set_tags(tags);
-
// Read the stream of tokens into the TokenStream object for script
// snapshots as we made a copy of token stream.
if (kind == Snapshot::kScript) {
@@ -924,9 +878,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -953,17 +905,12 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate script object.
Script& script = Script::ZoneHandle(reader->zone(), NEW_OBJECT(Script));
reader->AddBackRef(object_id, &script, kIsDeserialized);
- // Set the object tags.
- script.set_tags(tags);
-
script.StoreNonPointer(&script.raw_ptr()->line_offset_,
reader->Read<int32_t>());
script.StoreNonPointer(&script.raw_ptr()->col_offset_,
@@ -993,9 +940,7 @@
Snapshot::Kind kind) {
ASSERT(writer != NULL);
ASSERT(tokens_ != TokenStream::null());
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -1025,18 +970,16 @@
Library& library = Library::ZoneHandle(reader->zone(), Library::null());
reader->AddBackRef(object_id, &library, kIsDeserialized);
- if ((kind == Snapshot::kScript) && RawObject::IsCreatedFromSnapshot(tags)) {
- ASSERT(kind != Snapshot::kFull);
+ bool is_in_fullsnapshot = reader->Read<bool>();
+ if ((kind == Snapshot::kScript) && is_in_fullsnapshot) {
// Lookup the object as it should already exist in the heap.
*reader->StringHandle() ^= reader->ReadObjectImpl(kAsInlinedObject);
library = Library::LookupLibrary(*reader->StringHandle());
+ ASSERT(library.is_in_fullsnapshot());
} else {
// Allocate library object.
library = NEW_OBJECT(Library);
- // Set the object tags.
- library.set_tags(tags);
-
// Set all non object fields.
library.StoreNonPointer(&library.raw_ptr()->index_,
reader->ReadClassIDValue());
@@ -1052,6 +995,11 @@
reader->Read<bool>());
library.StoreNonPointer(&library.raw_ptr()->debuggable_,
reader->Read<bool>());
+ if (kind == Snapshot::kFull) {
+ is_in_fullsnapshot = true;
+ }
+ library.StoreNonPointer(&library.raw_ptr()->is_in_fullsnapshot_,
+ is_in_fullsnapshot);
// The native resolver and symbolizer are not serialized.
library.set_native_entry_resolver(NULL);
library.set_native_entry_symbol_resolver(NULL);
@@ -1088,12 +1036,16 @@
writer->WriteVMIsolateObject(kLibraryCid);
writer->WriteTags(writer->GetObjectTags(this));
- if ((kind == Snapshot::kScript) &&
- RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) {
- ASSERT(kind != Snapshot::kFull);
+ // Write out the boolean is_in_fullsnapshot_ first as this will
+ // help the reader decide how the rest of the information needs
+ // to be interpreted.
+ writer->Write<bool>(ptr()->is_in_fullsnapshot_);
+
+ if ((kind == Snapshot::kScript) && ptr()->is_in_fullsnapshot_) {
// Write out library URL so that it can be looked up when reading.
writer->WriteObjectImpl(ptr()->url_, kAsInlinedObject);
} else {
+ ASSERT((kind == Snapshot::kFull) || !ptr()->is_in_fullsnapshot_);
// Write out all non object fields.
writer->WriteClassIDValue(ptr()->index_);
writer->WriteClassIDValue(ptr()->num_anonymous_);
@@ -1120,18 +1072,13 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate library prefix object.
LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(
reader->zone(), NEW_OBJECT(LibraryPrefix));
reader->AddBackRef(object_id, &prefix, kIsDeserialized);
- // Set the object tags.
- prefix.set_tags(tags);
-
// Set all non object fields.
prefix.StoreNonPointer(&prefix.raw_ptr()->num_imports_,
reader->Read<int16_t>());
@@ -1157,9 +1104,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -1184,18 +1129,13 @@
intptr_t tags,
Snapshot::Kind kind) {
ASSERT(reader != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(tags)) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Allocate Namespace object.
Namespace& ns = Namespace::ZoneHandle(
reader->zone(), NEW_OBJECT(Namespace));
reader->AddBackRef(object_id, &ns, kIsDeserialized);
- // Set the object tags.
- ns.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -1214,9 +1154,7 @@
intptr_t object_id,
Snapshot::Kind kind) {
ASSERT(writer != NULL);
- ASSERT(((kind == Snapshot::kScript) &&
- !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
- (kind == Snapshot::kFull));
+ ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
// Write out the serialization header value for this object.
writer->WriteInlinedObjectHeader(object_id);
@@ -1292,9 +1230,6 @@
PcDescriptors::New(length));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
if (result.Length() > 0) {
NoSafepointScope no_safepoint;
intptr_t len = result.Length();
@@ -1339,9 +1274,6 @@
Stackmap::New(length, register_bit_count, pc_offset));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
if (result.Length() > 0) {
NoSafepointScope no_safepoint;
intptr_t len = (result.Length() + 7) / 8;
@@ -1386,9 +1318,6 @@
LocalVarDescriptors::New(num_entries));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
for (intptr_t i = 0; i < num_entries; i++) {
(*reader->StringHandle()) ^= reader->ReadObjectImpl(kAsReference);
result.StorePointer(result.raw()->nameAddrAt(i),
@@ -1442,9 +1371,6 @@
ExceptionHandlers::New(*reader->ArrayHandle()));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
if (result.num_entries() > 0) {
NoSafepointScope no_safepoint;
const intptr_t len =
@@ -1492,9 +1418,6 @@
} else {
context ^= NEW_OBJECT_WITH_LEN(Context, num_vars);
- // Set the object tags.
- context.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -1623,9 +1546,6 @@
ApiError::ZoneHandle(reader->zone(), NEW_OBJECT(ApiError));
reader->AddBackRef(object_id, &api_error, kIsDeserialized);
- // Set the object tags.
- api_error.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -1669,9 +1589,6 @@
LanguageError::ZoneHandle(reader->zone(), NEW_OBJECT(LanguageError));
reader->AddBackRef(object_id, &language_error, kIsDeserialized);
- // Set the object tags.
- language_error.set_tags(tags);
-
// Set all non object fields.
language_error.set_token_pos(reader->Read<int32_t>());
language_error.set_kind(reader->Read<uint8_t>());
@@ -1721,9 +1638,6 @@
reader->zone(), NEW_OBJECT(UnhandledException));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
@@ -1780,26 +1694,20 @@
Instance& obj = Instance::ZoneHandle(reader->zone(), Instance::null());
if (kind == Snapshot::kFull) {
obj = reader->NewInstance();
+ // Set the canonical bit.
+ if (RawObject::IsCanonical(tags)) {
+ obj.SetCanonical();
+ }
} else {
obj ^= Object::Allocate(kInstanceCid,
Instance::InstanceSize(),
HEAP_SPACE(kind));
- // When reading a script snapshot we need to canonicalize only those object
- // references that are objects from the core library (loaded from a
- // full snapshot). Objects that are only in the script need not be
- // canonicalized as they are already canonical.
- // When reading a message snapshot we always have to canonicalize.
- if (RawObject::IsCanonical(tags) &&
- (RawObject::IsCreatedFromSnapshot(tags) ||
- (kind == Snapshot::kMessage))) {
+ if (RawObject::IsCanonical(tags)) {
obj = obj.CheckAndCanonicalize(NULL);
}
}
reader->AddBackRef(object_id, &obj, kIsDeserialized);
- // Set the object tags.
- obj.set_tags(tags);
-
return obj.raw();
}
@@ -1840,25 +1748,21 @@
Mint& mint = Mint::ZoneHandle(reader->zone(), Mint::null());
if (kind == Snapshot::kFull) {
mint = reader->NewMint(value);
- // Set the object tags.
- mint.set_tags(tags);
+ // Set the canonical bit.
+ if (RawObject::IsCanonical(tags)) {
+ mint.SetCanonical();
+ }
} else {
// When reading a script snapshot we need to canonicalize only those object
// references that are objects from the core library (loaded from a
// full snapshot). Objects that are only in the script need not be
// canonicalized as they are already canonical.
// When reading a message snapshot we always have to canonicalize.
- if (RawObject::IsCanonical(tags) &&
- (RawObject::IsCreatedFromSnapshot(tags) ||
- (kind == Snapshot::kMessage))) {
+ if (RawObject::IsCanonical(tags)) {
mint = Mint::NewCanonical(value);
- ASSERT(mint.IsCanonical() &&
- (kind == Snapshot::kMessage ||
- RawObject::IsCreatedFromSnapshot(mint.raw()->ptr()->tags_)));
+ ASSERT(mint.IsCanonical());
} else {
mint = Mint::New(value, HEAP_SPACE(kind));
- // Set the object tags.
- mint.set_tags(tags);
}
}
reader->AddBackRef(object_id, &mint, kIsDeserialized);
@@ -1906,25 +1810,17 @@
// If it is a canonical constant make it one.
// When reading a full snapshot we don't need to canonicalize the object
// as it would already be a canonical object.
- // When reading a script snapshot we need to canonicalize only those object
- // references that are objects from the core library (loaded from a
- // full snapshot). Objects that are only in the script need not be
- // canonicalized as they are already canonical.
- // When reading a message snapshot we always have to canonicalize the object.
- if (kind == Snapshot::kFull) {
- // Set the object tags.
- obj.set_tags(tags);
- } else if (RawObject::IsCanonical(tags) &&
- (RawObject::IsCreatedFromSnapshot(tags) ||
- (kind == Snapshot::kMessage))) {
- obj ^= obj.CheckAndCanonicalize(NULL);
- ASSERT(!obj.IsNull());
- ASSERT(obj.IsCanonical() &&
- (kind == Snapshot::kMessage ||
- RawObject::IsCreatedFromSnapshot(obj.raw()->ptr()->tags_)));
- } else {
- // Set the object tags.
- obj.set_tags(tags);
+ // When reading a script snapshot or a message snapshot we always have
+ // to canonicalize the object.
+ if (RawObject::IsCanonical(tags)) {
+ if (kind == Snapshot::kFull) {
+ // Set the canonical bit.
+ obj.SetCanonical();
+ } else {
+ obj ^= obj.CheckAndCanonicalize(NULL);
+ ASSERT(!obj.IsNull());
+ ASSERT(obj.IsCanonical());
+ }
}
return obj.raw();
}
@@ -1961,23 +1857,20 @@
Double& dbl = Double::ZoneHandle(reader->zone(), Double::null());
if (kind == Snapshot::kFull) {
dbl = reader->NewDouble(value);
- // Set the object tags.
- dbl.set_tags(tags);
+ // Set the canonical bit.
+ if (RawObject::IsCanonical(tags)) {
+ dbl.SetCanonical();
+ }
} else {
// When reading a script snapshot we need to canonicalize only those object
// references that are objects from the core library (loaded from a
// full snapshot). Objects that are only in the script need not be
// canonicalized as they are already canonical.
- if (RawObject::IsCanonical(tags) &&
- RawObject::IsCreatedFromSnapshot(tags)) {
+ if (RawObject::IsCanonical(tags)) {
dbl = Double::NewCanonical(value);
- ASSERT(dbl.IsCanonical() &&
- (kind == Snapshot::kMessage ||
- RawObject::IsCreatedFromSnapshot(dbl.raw()->ptr()->tags_)));
+ ASSERT(dbl.IsCanonical());
} else {
dbl = Double::New(value, HEAP_SPACE(kind));
- // Set the object tags.
- dbl.set_tags(tags);
}
}
reader->AddBackRef(object_id, &dbl, kIsDeserialized);
@@ -2037,7 +1930,6 @@
} else {
// Set up the string object.
*str_obj = StringType::New(len, HEAP_SPACE(kind));
- str_obj->set_tags(tags);
str_obj->SetHash(0); // Will get computed when needed.
if (len == 0) {
return;
@@ -2068,7 +1960,9 @@
ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0);
RawOneByteString* obj = reader->NewOneByteString(len);
str_obj = obj;
- str_obj.set_tags(tags);
+ if (RawObject::IsCanonical(tags)) {
+ str_obj.SetCanonical();
+ }
str_obj.SetHash(hash);
if (len > 0) {
uint8_t* raw_ptr = CharAddr(str_obj, 0);
@@ -2097,7 +1991,9 @@
if (kind == Snapshot::kFull) {
RawTwoByteString* obj = reader->NewTwoByteString(len);
str_obj = obj;
- str_obj.set_tags(tags);
+ if (RawObject::IsCanonical(tags)) {
+ str_obj.SetCanonical();
+ }
str_obj.SetHash(hash);
NoSafepointScope no_safepoint;
uint16_t* raw_ptr = (len > 0)? CharAddr(str_obj, 0) : NULL;
@@ -2286,7 +2182,11 @@
}
reader->ArrayReadFrom(object_id, *array, len, tags);
if (RawObject::IsCanonical(tags)) {
- *array ^= array->CheckAndCanonicalize(NULL);
+ if (kind == Snapshot::kFull) {
+ array->SetCanonical();
+ } else {
+ *array ^= array->CheckAndCanonicalize(NULL);
+ }
}
return raw(*array);
}
@@ -2332,12 +2232,22 @@
array = GrowableObjectArray::New(0, HEAP_SPACE(kind));
}
reader->AddBackRef(object_id, &array, kIsDeserialized);
- intptr_t length = reader->ReadSmiValue();
- array.SetLength(length);
+
+ // Read type arguments of growable array object.
+ const intptr_t typeargs_offset =
+ GrowableObjectArray::type_arguments_offset() / kWordSize;
+ *reader->TypeArgumentsHandle() ^=
+ reader->ReadObjectImpl(kAsInlinedObject, object_id, typeargs_offset);
+ array.StorePointer(&array.raw_ptr()->type_arguments_,
+ reader->TypeArgumentsHandle()->raw());
+
+ // Read length of growable array object.
+ array.SetLength(reader->ReadSmiValue());
+
+ // Read the backing array of growable array object.
*(reader->ArrayHandle()) ^= reader->ReadObjectImpl(kAsInlinedObject);
array.SetData(*(reader->ArrayHandle()));
- *(reader->TypeArgumentsHandle()) = reader->ArrayHandle()->GetTypeArguments();
- array.SetTypeArguments(*(reader->TypeArgumentsHandle()));
+
return array.raw();
}
@@ -2354,6 +2264,9 @@
writer->WriteIndexedObject(kGrowableObjectArrayCid);
writer->WriteTags(writer->GetObjectTags(this));
+ // Write out the type arguments field.
+ writer->WriteObjectImpl(ptr()->type_arguments_, kAsInlinedObject);
+
// Write out the used length field.
writer->Write<RawObject*>(ptr()->length_);
@@ -2379,13 +2292,10 @@
map = LinkedHashMap::NewUninitialized(HEAP_SPACE(kind));
}
reader->AddBackRef(object_id, &map, kIsDeserialized);
- // Set the object tags.
- map.set_tags(tags);
// Read the type arguments.
- intptr_t typeargs_offset =
- reinterpret_cast<RawObject**>(&map.raw()->ptr()->type_arguments_) -
- reinterpret_cast<RawObject**>(map.raw()->ptr());
+ const intptr_t typeargs_offset =
+ GrowableObjectArray::type_arguments_offset() / kWordSize;
*reader->TypeArgumentsHandle() ^=
reader->ReadObjectImpl(kAsInlinedObject, object_id, typeargs_offset);
map.SetTypeArguments(*reader->TypeArgumentsHandle());
@@ -2493,8 +2403,6 @@
simd = Float32x4::New(value0, value1, value2, value3, HEAP_SPACE(kind));
}
reader->AddBackRef(object_id, &simd, kIsDeserialized);
- // Set the object tags.
- simd.set_tags(tags);
return simd.raw();
}
@@ -2539,8 +2447,6 @@
simd = Int32x4::New(value0, value1, value2, value3, HEAP_SPACE(kind));
}
reader->AddBackRef(object_id, &simd, kIsDeserialized);
- // Set the object tags.
- simd.set_tags(tags);
return simd.raw();
}
@@ -2583,8 +2489,6 @@
simd = Float64x2::New(value0, value1, HEAP_SPACE(kind));
}
reader->AddBackRef(object_id, &simd, kIsDeserialized);
- // Set the object tags.
- simd.set_tags(tags);
return simd.raw();
}
@@ -2626,9 +2530,6 @@
: TypedData::New(cid, len, HEAP_SPACE(kind)));
reader->AddBackRef(object_id, &result, kIsDeserialized);
- // Set the object tags.
- result.set_tags(tags);
-
// Setup the array elements.
intptr_t element_size = ElementSizeInBytes(cid);
intptr_t length_in_bytes = len * element_size;
@@ -2977,9 +2878,6 @@
reader->zone(), JSRegExp::New(HEAP_SPACE(kind)));
reader->AddBackRef(object_id, ®ex, kIsDeserialized);
- // Set the object tags.
- regex.set_tags(tags);
-
// Read and Set all the other fields.
regex.StoreSmi(®ex.raw_ptr()->num_bracket_expressions_,
reader->ReadAsSmi());
@@ -3027,9 +2925,6 @@
reader->zone(), WeakProperty::New(HEAP_SPACE(kind)));
reader->AddBackRef(object_id, &weak_property, kIsDeserialized);
- // Set the object tags.
- weak_property.set_tags(tags);
-
// Set all the object fields.
// TODO(5411462): Need to assert No GC can happen here, even though
// allocations may happen.
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 6f4fd03..b40b477 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -2258,6 +2258,11 @@
static bool Resume(Isolate* isolate, JSONStream* js) {
const char* step_param = js->LookupParam("step");
if (isolate->message_handler()->paused_on_start()) {
+ // If the user is issuing a 'Over' or an 'Out' step, that is the
+ // same as a regular resume request.
+ if ((step_param != NULL) && (strcmp(step_param, "Into") == 0)) {
+ isolate->debugger()->EnterSingleStepMode();
+ }
isolate->message_handler()->set_pause_on_start(false);
if (Service::debug_stream.enabled()) {
ServiceEvent event(isolate, ServiceEvent::kResume);
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 98ae976..907e805 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -214,7 +214,9 @@
(*backward_references_)[i].set_state(kIsDeserialized);
}
}
- ProcessDeferredCanonicalizations();
+ if (kind() != Snapshot::kFull) {
+ ProcessDeferredCanonicalizations();
+ }
return obj.raw();
} else {
// An error occurred while reading, return the error object.
@@ -323,6 +325,11 @@
}
+bool SnapshotReader::is_vm_isolate() const {
+ return isolate_ == Dart::vm_isolate();
+}
+
+
RawObject* SnapshotReader::ReadObjectImpl(bool as_reference,
intptr_t patch_object_id,
intptr_t patch_offset) {
@@ -461,9 +468,6 @@
#undef SNAPSHOT_READ
default: UNREACHABLE(); break;
}
- if (kind_ == Snapshot::kFull) {
- pobj_.SetCreatedFromSnapshot();
- }
return pobj_.raw();
}
@@ -534,10 +538,14 @@
result->SetFieldAtOffset(offset, Object::null_object());
offset += kWordSize;
}
- result->SetCreatedFromSnapshot();
- } else if (RawObject::IsCanonical(tags)) {
- *result = result->CheckAndCanonicalize(NULL);
- ASSERT(!result->IsNull());
+ }
+ if (RawObject::IsCanonical(tags)) {
+ if (kind_ == Snapshot::kFull) {
+ result->SetCanonical();
+ } else {
+ *result = result->CheckAndCanonicalize(NULL);
+ ASSERT(!result->IsNull());
+ }
}
return result->raw();
} else if (header_id == kStaticImplicitClosureObjectId) {
@@ -576,9 +584,6 @@
#undef SNAPSHOT_READ
default: UNREACHABLE(); break;
}
- if (kind_ == Snapshot::kFull) {
- pobj_.SetCreatedFromSnapshot();
- }
AddPatchRecord(object_id, patch_object_id, patch_offset);
return pobj_.raw();
}
@@ -1069,6 +1074,7 @@
ASSERT(class_id != kIllegalCid);
tags = RawObject::ClassIdTag::update(class_id, tags);
tags = RawObject::SizeTag::update(size, tags);
+ tags = RawObject::VMHeapObjectTag::update(is_vm_isolate(), tags);
raw_obj->ptr()->tags_ = tags;
return raw_obj;
}
@@ -1155,28 +1161,36 @@
void SnapshotReader::ProcessDeferredCanonicalizations() {
- AbstractType& typeobj = AbstractType::Handle();
+ Type& typeobj = Type::Handle();
TypeArguments& typeargs = TypeArguments::Handle();
Object& newobj = Object::Handle();
for (intptr_t i = 0; i < backward_references_->length(); i++) {
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->IsAbstractType()) {
+ if (objref->IsType()) {
typeobj ^= objref->raw();
- typeobj.ClearCanonical();
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();
- typeargs.ClearCanonical();
newobj = typeargs.Canonicalize();
+ if ((newobj.raw() != typeargs.raw()) && !typeargs.IsRecursive()) {
+ needs_patching = true;
+ } else {
+ // Set Canonical bit.
+ objref->SetCanonical();
+ }
}
- if (newobj.raw() == objref->raw()) {
- // Restore Canonical bit.
- objref->SetCanonical();
- } else {
+ if (needs_patching) {
ZoneGrowableArray<intptr_t>* patches = backref.patch_records();
ASSERT(newobj.IsCanonical());
ASSERT(patches != NULL);
@@ -1199,13 +1213,9 @@
const Array& result,
intptr_t len,
intptr_t tags) {
- // Set the object tags.
- result.set_tags(tags);
-
// Setup the object fields.
const intptr_t typeargs_offset =
- reinterpret_cast<RawObject**>(&result.raw()->ptr()->type_arguments_) -
- reinterpret_cast<RawObject**>(result.raw()->ptr());
+ GrowableObjectArray::type_arguments_offset() / kWordSize;
*TypeArgumentsHandle() ^= ReadObjectImpl(kAsInlinedObject,
object_id,
typeargs_offset);
@@ -2044,11 +2054,6 @@
ASSERT(kind_ != Snapshot::kFull);
int class_id = cls->ptr()->id_;
ASSERT(!IsSingletonClassId(class_id) && !IsObjectStoreClassId(class_id));
- // TODO(5411462): Should restrict this to only core-lib classes in this
- // case.
- // Write out the class and tags information.
- WriteVMIsolateObject(kClassCid);
- WriteTags(GetObjectTags(cls));
// Write out the library url and class name.
RawLibrary* library = cls->ptr()->library_;
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 161da59..4b3a278 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -450,6 +450,8 @@
RawObject* VmIsolateSnapshotObject(intptr_t index) const;
+ bool is_vm_isolate() const;
+
Snapshot::Kind kind_; // Indicates type of snapshot(full, script, message).
Isolate* isolate_; // Current isolate.
Zone* zone_; // Zone for allocations while reading snapshot.
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 2843561..0c4ccaa 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -1055,7 +1055,6 @@
}
-#if 0
UNIT_TEST_CASE(CanonicalizationInScriptSnapshots) {
const char* kScriptChars =
"\n"
@@ -1166,7 +1165,6 @@
free(script_snapshot);
free(full_snapshot);
}
-#endif
static void IterateScripts(const Library& lib) {
diff --git a/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart b/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
index e5c8495..aa836ca 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
@@ -443,6 +443,10 @@
.invoke(#call, positionalArguments, namedArguments);
}
+ delegate(Invocation invocation) {
+ throw new UnimplementedError();
+ }
+
_loadField(String name) {
// TODO(ahe): What about lazily initialized fields? See
// [JsClassMirror.getField].
@@ -853,6 +857,10 @@
null, setterSymbol(fieldName), [arg], null);
}
+ delegate(Invocation invocation) {
+ throw new UnimplementedError();
+ }
+
List<ClassMirror> get superinterfaces => [mixin];
Map<Symbol, MethodMirror> get __constructors => _mixin.__constructors;
@@ -1512,6 +1520,10 @@
return _class.invoke(memberName, positionalArguments, namedArguments);
}
+ delegate(Invocation invocation) {
+ throw new UnimplementedError();
+ }
+
bool get isOriginalDeclaration => false;
ClassMirror get originalDeclaration => _class;
@@ -2028,6 +2040,10 @@
return reflect(mirror._invoke(positionalArguments, namedArguments));
}
+ delegate(Invocation invocation) {
+ throw new UnimplementedError();
+ }
+
bool get isOriginalDeclaration => true;
ClassMirror get originalDeclaration => this;
@@ -2609,6 +2625,7 @@
InstanceMirror getField(Symbol fieldName) => throw new UnimplementedError();
InstanceMirror setField(Symbol fieldName, Object value)
=> throw new UnimplementedError();
+ delegate(Invocation invocation) => throw new UnimplementedError();
List<TypeVariableMirror> get typeVariables => throw new UnimplementedError();
List<TypeMirror> get typeArguments => throw new UnimplementedError();
TypeMirror get originalDeclaration => throw new UnimplementedError();
diff --git a/sdk/lib/core/list.dart b/sdk/lib/core/list.dart
index 9d9a093..8e837f8 100644
--- a/sdk/lib/core/list.dart
+++ b/sdk/lib/core/list.dart
@@ -186,7 +186,7 @@
* Sorts this list according to the order specified by the [compare] function.
*
* The [compare] function must act as a [Comparator].
-
+ *
* List<String> numbers = ['one', 'two', 'three', 'four'];
* // Sort from shortest to longest.
* numbers.sort((x, y) => x.length.compareTo(y.length));
@@ -197,7 +197,7 @@
*
* List<int> nums = [13, 2, -11];
* nums.sort();
- nums.join(', '); // '-11, 2, 13'
+ * nums.join(', '); // '-11, 2, 13'
*/
void sort([int compare(E a, E b)]);
diff --git a/sdk/lib/mirrors/mirrors.dart b/sdk/lib/mirrors/mirrors.dart
index d0f8379..3480cde 100644
--- a/sdk/lib/mirrors/mirrors.dart
+++ b/sdk/lib/mirrors/mirrors.dart
@@ -446,6 +446,23 @@
*/
/* TODO(turnidge): Handle ambiguous names.*/
InstanceMirror setField(Symbol fieldName, Object value);
+
+ /**
+ * Perform [invocation] on [reflectee].
+ * Equivalent to
+ *
+ * if (invocation.isGetter) {
+ * return this.getField(invocation.memberName).reflectee;
+ * } else if (invocation.isSetter) {
+ * return this.setField(invocation.memberName,
+ * invocation.positionArguments[0]).reflectee;
+ * } else {
+ * return this.invoke(invocation.memberName,
+ * invocation.positionalArguments,
+ * invocation.namedArguments).reflectee;
+ * }
+ */
+ delegate(Invocation invocation);
}
/**
@@ -499,23 +516,6 @@
* by [other] are identical.
*/
bool operator == (other);
-
- /**
- * Perform [invocation] on [reflectee].
- * Equivalent to
- *
- * if (invocation.isGetter) {
- * return this.getField(invocation.memberName).reflectee;
- * } else if (invocation.isSetter) {
- * return this.setField(invocation.memberName,
- * invocation.positionArguments[0]).reflectee;
- * } else {
- * return this.invoke(invocation.memberName,
- * invocation.positionalArguments,
- * invocation.namedArguments).reflectee;
- * }
- */
- delegate(Invocation invocation);
}
/**
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index 41355d7..035100e 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -18,9 +18,6 @@
ping_pause_test: Skip # Resolve test issues
kill3_test: Pass, Fail # Bad test: expects total message order
-message3_test/constList_identical: RuntimeError # Issue 21816
-message3_test/constMap: RuntimeError # Issue 21816
-message3_test/constInstance: RuntimeError # Issue 21816
message3_test/byteBuffer: Crash # Issue 21818
message3_test/int32x4: Crash # Issue 21818
diff --git a/tests/isolate/message3_test.dart b/tests/isolate/message3_test.dart
index c07a7e6..4267cee 100644
--- a/tests/isolate/message3_test.dart
+++ b/tests/isolate/message3_test.dart
@@ -394,7 +394,7 @@
Expect.equals("field", f.field);
Expect.isFalse(identical(nonConstF, f));
});
- checks.add((x) { // g1.
+ checks.add((x) { // g2.
Expect.isTrue(x is G);
Expect.isFalse(identical(g1, x));
F f = x.field;
@@ -403,7 +403,7 @@
});
checks.add((x) { // g3.
Expect.isTrue(x is G);
- Expect.identical(g1, x); /// constInstance: continued
+ Expect.identical(g3, x); /// constInstance: continued
F f = x.field;
Expect.equals("field", f.field);
Expect.identical(constF, f); /// constInstance: continued
diff --git a/tests/language/language.status b/tests/language/language.status
index 55e186d..ce9ff2e 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -44,61 +44,6 @@
[ $compiler == none ]
dynamic_prefix_core_test/01: RuntimeError # Issue 12478
multiline_strings_test: Fail # Issue 23020
-conditional_method_invocation_test/12: Fail # Issue 23794
-conditional_method_invocation_test/13: Fail # Issue 23794
-conditional_method_invocation_test/14: Fail # Issue 23794
-conditional_method_invocation_test/15: Fail # Issue 23794
-conditional_method_invocation_test/16: Fail # Issue 23794
-conditional_method_invocation_test/17: Fail # Issue 23794
-conditional_method_invocation_test/18: Fail # Issue 23794
-conditional_method_invocation_test/19: Fail # Issue 23794
-conditional_property_access_test/10: Fail # Issue 23794
-conditional_property_access_test/11: Fail # Issue 23794
-conditional_property_access_test/12: Fail # Issue 23794
-conditional_property_access_test/13: Fail # Issue 23794
-conditional_property_access_test/14: Fail # Issue 23794
-conditional_property_access_test/15: Fail # Issue 23794
-conditional_property_access_test/16: Fail # Issue 23794
-conditional_property_access_test/17: Fail # Issue 23794
-conditional_property_assignment_test/23: Fail # Issue 23794
-conditional_property_assignment_test/24: Fail # Issue 23794
-conditional_property_assignment_test/25: Fail # Issue 23794
-conditional_property_assignment_test/26: Fail # Issue 23794
-conditional_property_assignment_test/27: Fail # Issue 23794
-conditional_property_assignment_test/28: Fail # Issue 23794
-conditional_property_assignment_test/29: Fail # Issue 23794
-conditional_property_assignment_test/30: Fail # Issue 23794
-conditional_property_assignment_test/31: Fail # Issue 23794
-conditional_property_assignment_test/32: Fail # Issue 23794
-conditional_property_assignment_test/33: Fail # Issue 23794
-conditional_property_assignment_test/34: Fail # Issue 23794
-conditional_property_assignment_test/35: Fail # Issue 23794
-conditional_property_increment_decrement_test/17: Fail # Issue 23794
-conditional_property_increment_decrement_test/18: Fail # Issue 23794
-conditional_property_increment_decrement_test/19: Fail # Issue 23794
-conditional_property_increment_decrement_test/20: Fail # Issue 23794
-conditional_property_increment_decrement_test/21: Fail # Issue 23794
-conditional_property_increment_decrement_test/22: Fail # Issue 23794
-conditional_property_increment_decrement_test/23: Fail # Issue 23794
-conditional_property_increment_decrement_test/24: Fail # Issue 23794
-conditional_property_increment_decrement_test/25: Fail # Issue 23794
-conditional_property_increment_decrement_test/26: Fail # Issue 23794
-conditional_property_increment_decrement_test/27: Fail # Issue 23794
-conditional_property_increment_decrement_test/28: Fail # Issue 23794
-conditional_property_increment_decrement_test/29: Fail # Issue 23794
-conditional_property_increment_decrement_test/30: Fail # Issue 23794
-conditional_property_increment_decrement_test/31: Fail # Issue 23794
-conditional_property_increment_decrement_test/32: Fail # Issue 23794
-conditional_property_increment_decrement_test/33: Fail # Issue 23794
-conditional_property_increment_decrement_test/34: Fail # Issue 23794
-conditional_property_increment_decrement_test/35: Fail # Issue 23794
-conditional_property_increment_decrement_test/36: Fail # Issue 23794
-conditional_property_increment_decrement_test/37: Fail # Issue 23794
-conditional_property_increment_decrement_test/38: Fail # Issue 23794
-conditional_property_increment_decrement_test/39: Fail # Issue 23794
-conditional_property_increment_decrement_test/40: Fail # Issue 23794
-if_null_assignment_behavior_test/31: Fail # Issue 23794
-if_null_assignment_behavior_test/32: Fail # Issue 23794
[ $compiler == none && ($runtime == vm || $runtime == drt || $runtime == dartium || $runtime == ContentShellOnAndroid) ]
dynamic_prefix_core_test/none: Fail # Issue 12478
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 69c9605..5e6a2b9 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -18,6 +18,8 @@
mirrors/closurization_equivalence_test: RuntimeError # Issue 6490
mirrors/constructor_kinds_test: RuntimeError # Issue 13799
mirrors/constructor_private_name_test: CompileTimeError # Issue 13597
+mirrors/delegate_class_test: RuntimeError
+mirrors/delegate_library_test: RuntimeError
mirrors/deferred_type_test: RuntimeError # Issue 6335
mirrors/empty_test: RuntimeError # Issue 6490
mirrors/enum_test: RuntimeError # Issue 6490
diff --git a/tests/lib/mirrors/delegate_class_test.dart b/tests/lib/mirrors/delegate_class_test.dart
new file mode 100644
index 0000000..652ee44
--- /dev/null
+++ b/tests/lib/mirrors/delegate_class_test.dart
@@ -0,0 +1,50 @@
+// 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.
+
+library test.delegate_class;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class C {
+ static method(a, b, c) => "$a-$b-$c";
+ static methodWithNamed(a, {b:'B', c}) => "$a-$b-$c";
+ static methodWithOpt(a, [b, c='C']) => "$a-$b-$c";
+ static get getter => 'g';
+ static set setter(x) {
+ field = x*2;
+ return 'unobservable value';
+ }
+ static var field;
+}
+
+class Proxy {
+ var targetMirror;
+ Proxy(this.targetMirror);
+ noSuchMethod(invocation) => targetMirror.delegate(invocation);
+}
+
+main() {
+ var proxy = new Proxy(reflectClass(C));
+ var result;
+
+ Expect.equals('X-Y-Z', proxy.method('X', 'Y', 'Z'));
+
+ Expect.equals('X-B-null', proxy.methodWithNamed('X'));
+ Expect.equals('X-Y-null', proxy.methodWithNamed('X', b: 'Y'));
+ Expect.equals('X-Y-Z', proxy.methodWithNamed('X', b: 'Y', c: 'Z'));
+
+ Expect.equals('X-null-C', proxy.methodWithOpt('X'));
+ Expect.equals('X-Y-C', proxy.methodWithOpt('X', 'Y'));
+ Expect.equals('X-Y-Z', proxy.methodWithOpt('X', 'Y', 'Z'));
+
+ Expect.equals('g', proxy.getter);
+
+ Expect.equals(5, proxy.setter = 5);
+ Expect.equals(10, proxy.field);
+
+ Expect.equals(5, proxy.field = 5);
+ Expect.equals(5, proxy.field);
+}
diff --git a/tests/lib/mirrors/delegate_library_test.dart b/tests/lib/mirrors/delegate_library_test.dart
new file mode 100644
index 0000000..7eb5a84
--- /dev/null
+++ b/tests/lib/mirrors/delegate_library_test.dart
@@ -0,0 +1,49 @@
+// 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.
+
+library test.delegate_library;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+method(a, b, c) => "$a-$b-$c";
+methodWithNamed(a, {b:'B', c}) => "$a-$b-$c";
+methodWithOpt(a, [b, c='C']) => "$a-$b-$c";
+get getter => 'g';
+set setter(x) {
+ field = x*2;
+ return 'unobservable value';
+}
+var field;
+
+
+class Proxy {
+ var targetMirror;
+ Proxy(this.targetMirror);
+ noSuchMethod(invocation) => targetMirror.delegate(invocation);
+}
+
+main() {
+ var proxy = new Proxy(reflectClass(Proxy).owner);
+ var result;
+
+ Expect.equals('X-Y-Z', proxy.method('X', 'Y', 'Z'));
+
+ Expect.equals('X-B-null', proxy.methodWithNamed('X'));
+ Expect.equals('X-Y-null', proxy.methodWithNamed('X', b: 'Y'));
+ Expect.equals('X-Y-Z', proxy.methodWithNamed('X', b: 'Y', c: 'Z'));
+
+ Expect.equals('X-null-C', proxy.methodWithOpt('X'));
+ Expect.equals('X-Y-C', proxy.methodWithOpt('X', 'Y'));
+ Expect.equals('X-Y-Z', proxy.methodWithOpt('X', 'Y', 'Z'));
+
+ Expect.equals('g', proxy.getter);
+
+ Expect.equals(5, proxy.setter = 5);
+ Expect.equals(10, proxy.field);
+
+ Expect.equals(5, proxy.field = 5);
+ Expect.equals(5, proxy.field);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 6dd51cf..d45b9a4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
MINOR 12
PATCH 0
PRERELEASE 5
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2