[vm/bytecode] Include source info and asserts into platform with bytecode

* Include source positions, source text (if provided by front-end, which is
  controlled by a separate option) and assertions into platform with bytecode.
  Also use environment defines if provided.

* Omit bytecode for synthetic static fields '_redirecting#' injected
  by front-end, as they contain invalid AST nodes (taking tear-off of
  a constructor). This is needed to avoid emitting bytecode which cannot be
  loaded.

* Correctly merge source information such as source text and line numbers
  in bytecode generator if the script was originally created for an
  anonymous mixin application without such information.

* Always include import URIs into source information, even if other information is
  excluded (for members in anonymous mixin applications / noSuchMethod forwarders /
  forwarding stubs).

* Set native resolvers for vm/cc/PrintJSON test, as printing all objects to JSON
  involves collecting token positions, which may read bytecode from platform and
  need native resolvers to process native method entries.

Change-Id: I971e880c25439c0ec69db3a231a0085ccd5d15ad
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/113780
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
This commit is contained in:
Alexander Markov 2019-08-21 01:08:24 +00:00 committed by commit-bot@chromium.org
parent 457c4349d0
commit 922c2bfbb0
6 changed files with 66 additions and 40 deletions

View file

@ -19,6 +19,7 @@ import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
import 'package:kernel/type_environment.dart' show SubtypeTester;
import 'package:vm/bytecode/gen_bytecode.dart' show generateBytecode;
import 'package:vm/bytecode/options.dart' show BytecodeOptions;
import 'package:front_end/src/api_prototype/compiler_options.dart'
show CompilerOptions;
@ -329,7 +330,12 @@ Future<void> compilePlatformInternal(CompilerContext c, Uri fullOutput,
c.options.ticker.logMs("Wrote outline to ${outlineOutput.toFilePath()}");
if (c.options.bytecode) {
generateBytecode(result.component);
generateBytecode(result.component,
options: new BytecodeOptions(
enableAsserts: true,
emitSourceFiles: true,
emitSourcePositions: true,
environmentDefines: c.options.environmentDefines));
}
await writeComponentToFile(result.component, fullOutput,

View file

@ -220,22 +220,20 @@ class SourceFile {
static const hasLineStartsFlag = 1 << 0;
static const hasSourceFlag = 1 << 1;
int flags;
final ObjectHandle importUri;
final LineStarts lineStarts;
final String source;
LineStarts lineStarts;
String source;
SourceFile(this.importUri, this.lineStarts, this.source) {
flags = 0;
SourceFile(this.importUri, [this.lineStarts, this.source]);
void write(BufferedWriter writer) {
int flags = 0;
if (lineStarts != null) {
flags |= hasLineStartsFlag;
}
if (source != null && source != '') {
flags |= hasSourceFlag;
}
}
void write(BufferedWriter writer) {
writer.writePackedUInt30(flags);
writer.writePackedObject(importUri);
if ((flags & hasLineStartsFlag) != 0) {
@ -261,11 +259,13 @@ class SourceFile {
@override
String toString() {
final StringBuffer sb = new StringBuffer();
sb.writeln('SourceFile $importUri');
sb.writeln('------------------------------------------------');
sb.write(source);
sb.writeln('------------------------------------------------');
sb.writeln(lineStarts);
sb.write('source: import-uri $importUri');
if (source != null && source != '') {
sb.write(', ${source.length} text chars');
}
if (lineStarts != null) {
sb.write(', ${lineStarts.lineStarts.length} line starts');
}
return sb.toString();
}
}

View file

@ -211,30 +211,33 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
return members;
}
ObjectHandle getScript(Uri uri, bool includeSource) {
ObjectHandle getScript(Uri uri, bool includeSourceInfo) {
SourceFile source;
if (includeSource &&
(options.emitSourceFiles || options.emitSourcePositions)) {
source = bytecodeComponent.uriToSource[uri];
if (source == null) {
final astSource = astUriToSource[uri];
if (astSource != null) {
if (options.emitSourceFiles || options.emitSourcePositions) {
final astSource = astUriToSource[uri];
if (astSource != null) {
source = bytecodeComponent.uriToSource[uri];
if (source == null) {
final importUri =
objectTable.getNameHandle(null, astSource.importUri.toString());
LineStarts lineStarts;
if (options.emitSourcePositions) {
lineStarts = new LineStarts(astSource.lineStarts);
bytecodeComponent.lineStarts.add(lineStarts);
}
String text = '';
if (options.emitSourceFiles) {
text = astSource.cachedText ??
utf8.decode(astSource.source, allowMalformed: true);
}
source = new SourceFile(importUri, lineStarts, text);
source = new SourceFile(importUri);
bytecodeComponent.sourceFiles.add(source);
bytecodeComponent.uriToSource[uri] = source;
}
if (options.emitSourcePositions &&
includeSourceInfo &&
source.lineStarts == null) {
LineStarts lineStarts = new LineStarts(astSource.lineStarts);
bytecodeComponent.lineStarts.add(lineStarts);
source.lineStarts = lineStarts;
}
if (options.emitSourceFiles &&
includeSourceInfo &&
source.source == null) {
String text = astSource.cachedText ??
utf8.decode(astSource.source, allowMalformed: true);
source.source = text;
}
}
}
return objectTable.getScriptHandle(uri, source);
@ -777,6 +780,16 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
return false;
}
if (member is Field) {
// TODO(dartbug.com/34277)
// Front-end inserts synthetic static fields "_redirecting#" to record
// information about redirecting constructors in kernel.
// The problem is that initializers of these synthetic static fields
// contain incorrect kernel AST, e.g. StaticGet which takes tear-off
// of a constructor. Do not generate bytecode for them, as they should
// never be used.
if (member.isStatic && member.name.name == "_redirecting#") {
return false;
}
return hasInitializerCode(member);
}
return true;
@ -3083,6 +3096,8 @@ class BytecodeGenerator extends RecursiveVisitor<Null> {
} else if (target is Procedure) {
if (target.isGetter) {
_genDirectCall(target, objectTable.getArgDescHandle(0), 0, isGet: true);
} else if (target.isFactory || target.isRedirectingFactoryConstructor) {
throw 'Unexpected target for StaticGet: factory $target';
} else {
asm.emitPushConstant(cp.addObjectRef(new TearOffConstant(target)));
}

View file

@ -1592,8 +1592,7 @@ class _ScriptHandle extends ObjectHandle {
bool operator ==(other) => other is _ScriptHandle && this.uri == other.uri;
@override
String toString() =>
"$uri${source != null ? '(source ${source.importUri})' : ''}";
String toString() => "$uri${source != null ? '($source)' : ''}";
}
class ObjectTable implements ObjectWriter, ObjectReader {

View file

@ -1155,12 +1155,6 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
}
// Mark as loaded and finalized.
cls.Finalize();
// Every class should have at least a constructor, unless it is a top level
// class or a typedef class. The Kernel frontend does not create an implicit
// constructor for abstract classes.
// Moreover, Dart 2 precompiler (TFA) can tree shake all members if unused.
ASSERT(FLAG_precompiled_mode || cls.IsTopLevel() || cls.IsTypedefClass() ||
cls.is_abstract() || (Array::Handle(cls.functions()).Length() > 0));
FinalizeMemberTypes(cls);
// Run additional checks after all types are finalized.
if (FLAG_use_cha_deopt) {

View file

@ -4,6 +4,9 @@
#include "include/dart_api.h"
#include "bin/builtin.h"
#include "bin/vmservice_impl.h"
#include "platform/globals.h"
#include "vm/class_finalizer.h"
@ -3974,6 +3977,15 @@ class ObjectAccumulator : public ObjectVisitor {
};
ISOLATE_UNIT_TEST_CASE(PrintJSON) {
// Set native resolvers in case we need to read native methods.
{
TransitionVMToNative transition(thread);
bin::Builtin::SetNativeResolver(bin::Builtin::kBuiltinLibrary);
bin::Builtin::SetNativeResolver(bin::Builtin::kIOLibrary);
bin::Builtin::SetNativeResolver(bin::Builtin::kCLILibrary);
bin::VmService::SetNativeResolver();
}
Heap* heap = Isolate::Current()->heap();
heap->CollectAllGarbage();
GrowableArray<Object*> objects;