From d6c6d12ebf71a918279e1bdb420fedbc1d560a35 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Fri, 4 Oct 2019 10:11:30 +0000 Subject: [PATCH] Revert "[dartdevc] cleaning up unused web files" This reverts commit e866f043cfc95970b20e14487e27691d3351d3db. Reason for revert: Breaks google3. Original change's description: > [dartdevc] cleaning up unused web files > > Dependent on these google3 changes: https://critique.corp.google.com/#review/272749649 > > Change-Id: I9e89142cd5b2a619acfc35badb9cf3c549b3be9c > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/119587 > Reviewed-by: Konstantin Shcheglov > Commit-Queue: Mark Zhou TBR=scheglov@google.com,vsm@google.com,markzipan@google.com Change-Id: I1db094d94d699d4d18c4091f57c7cb775eb95dd5 No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120040 Reviewed-by: David Morgan Commit-Queue: David Morgan --- CHANGELOG.md | 7 - pkg/dev_compiler/CHANGELOG.md | 72 ++ .../lib/src/kernel/analyzer_to_kernel.dart | 917 ++++++++++++++++++ pkg/dev_compiler/lib/src/kernel/command.dart | 30 +- pkg/dev_compiler/lib/src/kernel/compiler.dart | 2 +- .../lib/src/kernel/kernel_helpers.dart | 3 +- pkg/dev_compiler/web/index.html | 10 + pkg/dev_compiler/web/main.dart | 86 ++ pkg/dev_compiler/web/web_command.dart | 313 ++++++ sdk/BUILD.gn | 24 +- sdk_nnbd/BUILD.gn | 35 +- 11 files changed, 1472 insertions(+), 27 deletions(-) create mode 100644 pkg/dev_compiler/CHANGELOG.md create mode 100644 pkg/dev_compiler/lib/src/kernel/analyzer_to_kernel.dart create mode 100644 pkg/dev_compiler/web/index.html create mode 100755 pkg/dev_compiler/web/main.dart create mode 100644 pkg/dev_compiler/web/web_command.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 78aafb85393..a67c82324a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,13 +82,6 @@ main() { foo(() {}); } ### Dart VM -### Dart for the Web - -#### Dart Dev Compiler (DDC) - -* Kernel DDC will no longer accept non-dill files as summary inputs. -* Removed support for the deprecated web extension. - ### Tools #### Pub diff --git a/pkg/dev_compiler/CHANGELOG.md b/pkg/dev_compiler/CHANGELOG.md new file mode 100644 index 00000000000..e7da8f1c4dd --- /dev/null +++ b/pkg/dev_compiler/CHANGELOG.md @@ -0,0 +1,72 @@ +# dev_compiler changelog + +## next release +- add support for AMD modules and make it the default. +- precompile the SDK in AMD, CommonJS, and ES6 flavors. +- legacy module format is deprecated. +- remove --package-paths option + +## 0.1.24 +- workaround breaking change on requestAnimationFrame + +## 0.1.23 +- updates for the latest analyzer +- removal of deprecated functionality (server mode) in prep for refactoring + +## 0.1.22 +- fixes to support the latest analyzer +- improvements in compile speed +- bug fixes on function / closure handling + +## 0.1.21 +- bug fix for dart:js constructor invocation + +## 0.1.20 +- support new StackTrace.current method + +## 0.1.19 +- support for dom libraries (dart:html, etc) + +## 0.1.18 +- dart:typed_data support +- preliminary TS / Closure output support +- various runtime typing fixes + +## 0.1.17 +- preliminary node module support +- support for compiling / serving multiple html files + +## 0.1.16 +- rolled analyzer to 0.27.2-alpha.1 +- fixes for static fields + +## 0.1.15 +- codegen fixes for dart:convert (json decode) and dart:math (max, min) + +## 0.1.14 +- updates to unpin analyzer and move forward to ^0.27.1+2 + +## 0.1.13 +- various fixes in js codegen +- pinned to analyzer 0.26.2+1 to avoid breaking upstream changes + +## 0.1.12 +- fixes for babel +- fixes toward new js interop + +## 0.1.11 +- moved js runtime files to lib/runtime/dart (`dart_runtime.js` -> `dart/_runtime.js`) +- bug fix to source maps +- initial support for f-bound quantification patterns + +## 0.1.10 +- added an `--html-report` option to create a file summarizing compilation + issues +- added a `-f` alias to the `--force-compile` command line option +- removed many info level messages that were informational to the DDC team + +## 0.1.9 + +## 0.1.8 +- added a `--version` command-line option +- added a new entry-point - `dev_compiler` - that aliases to `dartdevc` diff --git a/pkg/dev_compiler/lib/src/kernel/analyzer_to_kernel.dart b/pkg/dev_compiler/lib/src/kernel/analyzer_to_kernel.dart new file mode 100644 index 00000000000..87caa7716e7 --- /dev/null +++ b/pkg/dev_compiler/lib/src/kernel/analyzer_to_kernel.dart @@ -0,0 +1,917 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:collection'; +import 'dart:core' hide MapEntry; + +import 'package:analyzer/dart/element/element.dart' as a; +import 'package:analyzer/dart/element/type.dart' as a; +import 'package:analyzer/file_system/physical_file_system.dart' as a; +import 'package:analyzer/src/context/context.dart' as a; +import 'package:analyzer/src/dart/analysis/restricted_analysis_context.dart' + as a; +import 'package:analyzer/src/dart/element/element.dart' as a; +import 'package:analyzer/src/dart/element/member.dart' as a; +import 'package:analyzer/src/dart/element/type.dart' as a; +import 'package:analyzer/src/generated/constant.dart' as a; +import 'package:analyzer/src/generated/engine.dart' as a; +import 'package:analyzer/src/generated/resolver.dart' as a + show NamespaceBuilder, TypeProvider; +import 'package:analyzer/src/generated/source.dart' as a; +import 'package:analyzer/src/generated/type_system.dart' as a; +import 'package:analyzer/src/summary/idl.dart' as a; +import 'package:analyzer/src/summary/package_bundle_reader.dart' as a; +import 'package:analyzer/src/summary/summary_sdk.dart' as a; +import 'package:analyzer/src/summary2/linked_bundle_context.dart' as a; +import 'package:analyzer/src/summary2/linked_element_factory.dart' as a; +import 'package:analyzer/src/summary2/reference.dart' as a; +import 'package:front_end/src/api_unstable/ddc.dart' + show RedirectingFactoryBody; +import 'package:kernel/kernel.dart'; +import 'package:kernel/type_algebra.dart'; + +import '../analyzer/type_utilities.dart' hide freeTypeParameters; +import 'type_table.dart'; + +/// Converts an Analyzer summary file to a Kernel [Component]. +/// +/// The first step is to use Analyzer's [a.StoreBasedSummaryResynthesizer] to +/// deserialize the summary file into an [a.Element] model (that way we don't +/// depend directly on the file format). Once we have elements, we visit them +/// and construct the corresponding Kernel [Node]s. +/// +/// The main entry points are [convertSdk] and [convertSummaries], which +/// convert the SDK and input summaries, respectively. +/// +/// Because we only need to convert summaries, we do not need to handle method +/// bodies. This lets us avoid the complexity of converting Analyzer AST nodes +/// (e.g. expressions, statements). +/// +/// For constants we use Analyzer's constant evaluator compute the value from +/// the data in the summary, and then create the appropriate Kernel node to +/// reconstruct the constant (e.g. ListLiteral, ConstructorInvocation, etc). +/// See [_visitConstant] for more information. +/// +/// When something refers to an element, we normally create the [Reference] but +/// leave its corresponding [NamedNode] empty until that element is visited and +/// creates the Kernel node. This takes care of cycles, and avoids recursing too +/// deeply as we convert elements. +/// +/// Sometimes we need to convert an element eagerly (e.g. if we need to call +/// members on an [InterfaceType] or [Supertype], we need to create its [Class] +/// node). In that case we handle cycles in the visit method (e.g. +/// [visitClassElement]) by creating the node and linking it to its reference +/// before visiting anything else that might recurse. +/// +/// Special care must be taken to make sure we link up all [Reference]s with +/// their corresponding [NamedNode]. If we don't do this [verifyReferences] +/// will throw an error. The fix is to figure out why we didn't visit the +/// element for that reference (often this is due to Analyzer's synthetic +/// fields/accessor elements; care must be taken to always reference the real +/// element). +/// +/// Because we're using Analyzer's summary resynthesizer, conversion is all or +/// nothing: all summaries must be in Analyzer format, including the SDK. +/// Now that we have this implementation, it may be possible to port code from +/// Analyzer and modify it to resynthesize directly into Kernel trees, if we +/// ever need to support a mix of Kernel and Analyzer summary files. +class AnalyzerToKernel { + final a.LinkedElementFactory _resynth; + final a.SummaryDataStore _summaryData; + final a.TypeProvider types; + final a.Dart2TypeSystem rules; + + final _references = HashMap(); + final _typeParams = HashMap(); + final _namespaceBuilder = a.NamespaceBuilder(); + + AnalyzerToKernel._(this._resynth, this._summaryData) + : types = _resynth.analysisContext.typeProvider, + rules = _resynth.analysisContext.typeSystem as a.Dart2TypeSystem; + + /// Create an Analyzer summary to Kernel tree converter, using the provided + /// [analyzerSdkSummary] and [summaryPaths]. + /// + /// Once the converter is created, [convertSdk] should be called to convert + /// & return the SDK, followed by [convertSummaries] to convert & return the + /// converted summaries. + factory AnalyzerToKernel( + String analyzerSdkSummary, List summaryPaths) { + var summaryData = a.SummaryDataStore(summaryPaths, + resourceProvider: a.PhysicalResourceProvider.INSTANCE, + disallowOverlappingSummaries: false); + var resynthesizer = + _createSummaryResynthesizer(summaryData, analyzerSdkSummary); + return AnalyzerToKernel._(resynthesizer, summaryData); + } + + /// Converts the SDK summary to a Kernel component and returns it. + Component convertSdk() { + // _createContextForSummaries puts the SDK summary last in the summary data. + var sdkBundle = _summaryData.bundles.last; + assert(sdkBundle.linkedLibraryUris.every((u) => u.startsWith('dart:'))); + var result = _toComponent(sdkBundle); + verifyReferences(); + return result; + } + + /// Converts the input summaries to Kernel components and return them. + /// + /// [convertSdk] must be called before this. + List convertSummaries() { + // Take all summaries except the SDK one, which is placed last in the list + // by _createContextForSummaries. + var bundles = _summaryData.bundles.take(_summaryData.bundles.length - 1); + var result = bundles.map(_toComponent).toList(); + verifyReferences(); // assumption: convertSdk() is called first + return result; + } + + void verifyReferences() { + _references.forEach((element, reference) { + // Ensure each reference has a corresponding node. + // + // If it's missing a node, CFE will fail and it is difficult to debug at + // that point because the name and element cannot be accessed. + // + // Typically this error means: + // - we didn't visit an element. + // - we didn't set the `reference: _reference(e)` for the Kernel node. + // - we referenced a synthetic element by mistake, such as referencing the + // synthetic getter/setter, when we should've used the field. + if (reference.node == null) { + throw StateError('missing node for reference, element was: $element' + + (element.isSynthetic ? ' (synthetic)' : '')); + } + }); + } + + Component _toComponent(a.PackageBundle bundle) { + var libraries = []; + var uriToSource = {}; + + void addCompilationUnit(a.CompilationUnitElement unit) { + uriToSource[unit.source.uri] = Source( + unit.lineInfo.lineStarts, + [], + unit.uri != null ? Uri.base.resolve(unit.uri) : unit.source.uri, + unit.source.uri); + } + + for (var uri in bundle.unlinkedUnitUris) { + if (_summaryData.isPartUnit(uri)) { + // Library parts are handled by their corresponding library. + continue; + } + + var element = _resynth.libraryOfUri(uri); + libraries.add(visitLibraryElement(element)); + addCompilationUnit(element.definingCompilationUnit); + element.parts.forEach(addCompilationUnit); + } + return Component(libraries: libraries, uriToSource: uriToSource); + } + + Class visitClassElement(a.ClassElement e, [Library library]) { + var ref = _reference(e); + if (ref.node != null) return ref.asClass; + + // Construct the Class first and link the reference. This ensures the + // (not yet finished) Class node will be returned on the line above, if we + // happen to re-enter this visit method. + var class_ = Class( + name: e.name, + isAbstract: e.isAbstract, + fileUri: e.source.uri, + reference: ref); + + // Classes can be visited before their library (e.g. because they're a + // supertype of another class), so make sure to visit the library now. + library ??= visitLibraryElement(e.library); + library.addClass(class_); + + class_.isMixinDeclaration = e.isMixin; + class_.typeParameters + .addAll(e.typeParameters.map(visitTypeParameterElement)); + + setParents(class_.typeParameters, class_); + class_.implementedTypes.addAll(e.interfaces.map(_typeToSupertype)); + + var fields = class_.fields; + var constructors = class_.constructors; + var procedures = class_.procedures; + + fields.addAll(e.fields.where((f) => !f.isSynthetic).map(visitFieldElement)); + + var redirectingFactories = []; + for (var ctor in e.constructors) { + if (ctor.isFactory) { + var factory_ = _visitFactory(ctor); + procedures.add(factory_); + if (ctor.redirectedConstructor != null) { + redirectingFactories.add(factory_); + } + } else { + constructors.add(visitConstructorElement(ctor)); + } + } + if (redirectingFactories.isNotEmpty) { + fields.add(_createRedirectingFactoryField(redirectingFactories, e)); + } + procedures.addAll(e.methods.map(visitMethodElement)); + procedures.addAll(e.accessors + .where((a) => !a.isSynthetic) + .map(visitPropertyAccessorElement)); + + setParents(fields, class_); + setParents(constructors, class_); + setParents(procedures, class_); + + if (e.isMixinApplication) { + class_.mixedInType = _typeToSupertype(e.mixins.last); + } + + var supertype = _typeToSupertype(e.supertype); + class_.supertype = _unrollMixinClasses(e, supertype, library); + _visitAnnotations(e.metadata, class_.addAnnotation); + + // TODO(jmesserly): do we need covariance check stubs? We may be okay as + // since we're only handling dependencies here. + // + // But this may lead to redundant stubs (if CFE doesn't see one on a + // superclass) and/or break some assumptions in CFE. + return class_; + } + + Supertype _unrollMixinClasses( + a.ClassElement e, Supertype supertype, Library library) { + // TODO(jmesserly): is this enough for mixin desugaring? It only does + // enough to create the intermediate classes. + + // Documentation below assumes the given mixin application is in one of + // these forms: + // + // class C extends S with M1, M2, M3; + // class Named = S with M1, M2, M3; + // + // When we refer to the subclass, we mean `C` or `Named`. + + /// The number of mixin classes to unroll. + /// + /// Named mixin applications have one less class. This can be illustrated + /// here: + /// + /// class C extends S with M1, M2, M3 {} + /// class Named = S with M1, M2, M3; + /// + /// For `C` we unroll 3 classes: _C&S&M1, _C&S&M1&M2, _C&S&M1&M2&M3. + /// For `Named` we unroll 2 classes: _Named&S&M1, _Named&S&M1&M2. + /// + /// The classes themselves will be generated as: + /// + /// class C extends _C&S&M1&M2&M3 {} + /// class Named = _Named&S&M1&M2 with M3; + /// + var unrollLength = e.mixins.length; + if (e.isMixinApplication) unrollLength--; + if (unrollLength <= 0) return supertype; + + /// The mixin application's synthetic name. + /// + /// The full name of the mixin application is obtained by prepending the + /// name of the subclass (`C` or `Named` in the above examples) to the + /// running name. For the example `C`, that leads to these names: + /// + /// 1. `_C&S&M1` + /// 2. `_C&S&M1&M2` + /// 3. `_C&S&M1&M2&M3`. + var runningName = '_${e.name}&${e.supertype.name}'; + + /// The type variables used in the current supertype and mixin, or null + /// if this class doesn't have any type parameters. + var usedTypeVars = e.typeParameters.isNotEmpty + ? freeTypeParameters(supertype.asInterfaceType) + : null; + + for (int i = 0; i < unrollLength; i++) { + var mixin = e.mixins[i]; + runningName += "&${mixin.name}"; + + var mixedInType = _typeToSupertype(mixin); + List typeParameters; + if (usedTypeVars != null) { + // Any type params used by superclasses will continue to be used, plus + // anything additional that this mixin uses. + usedTypeVars.addAll(freeTypeParameters(mixedInType.asInterfaceType)); + if (usedTypeVars.isNotEmpty) { + // Make fresh type parameters for this class, and then substitute them + // into supertype and mixin type arguments (if any). + var fresh = getFreshTypeParameters(usedTypeVars.toList()); + typeParameters = fresh.freshTypeParameters; + supertype = fresh.substituteSuper(supertype); + mixedInType = fresh.substituteSuper(mixedInType); + } + } + + var c = Class( + name: runningName, + isAbstract: true, + mixedInType: mixedInType, + supertype: supertype, + typeParameters: typeParameters, + fileUri: e.source.uri); + + library.addClass(c); + + // Compute the superclass to use for the next iteration of this loop. + // + // Any type arguments are in terms of the original class type parameters. + // This allows us to perform consistent substitutions and have the correct + // type arguments for the final supertype (that we return). + supertype = Supertype( + c, + typeParameters != null + ? List.of(usedTypeVars.map((t) => TypeParameterType(t))) + : []); + } + + return supertype; + } + + Constructor visitConstructorElement(a.ConstructorElement e) { + assert(!e.isFactory); + var ref = _reference(e); + if (ref.node != null) return ref.asConstructor; + // By convention, instance constructors return `void` in Kernel. + var function = _createFunction(e)..returnType = const VoidType(); + var result = Constructor(function, + name: _getName(e), + isConst: e.isConst, + isExternal: e.isExternal, + isSynthetic: e.isSynthetic, + fileUri: e.source.uri, + reference: ref); + if (!result.isSynthetic) { + // TODO(jmesserly): CFE does not respect the synthetic bit on constructors + // so we set a bogus offset. This causes CFE to treat it as not synthetic. + // + // (The bug is in DillMemberBuilder.isSynthetic. Synthetic constructors + // have different semantics/optimizations in some cases, so it is + // important that the constructor is correctly marked.) + result.fileOffset = 1; + } + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + Procedure _visitFactory(a.ConstructorElement e) { + var ref = _reference(e); + if (ref.node != null) return ref.asProcedure; + + var result = Procedure.byReference(_getName(e), ProcedureKind.Factory, null, + isExternal: e.isExternal, + isConst: e.isConst, + isStatic: true, + fileUri: e.source.uri, + reference: ref); + + _visitAnnotations(e.metadata, result.addAnnotation); + + // Since the factory is static, we need to create fresh type parameters that + // match the ones in the enclosing class. + FreshTypeParameters fresh; + DartType Function(a.DartType) visitType; + + if (e.enclosingElement.typeParameters.isNotEmpty) { + fresh = getFreshTypeParameters( + visitClassElement(e.enclosingElement).typeParameters); + visitType = (t) => fresh.substitute(_visitDartType(t, ensureNode: true)); + } else { + visitType = _visitDartType; + } + + result.function = _createFunction(e, fresh?.freshTypeParameters, visitType); + result.function.parent = result; + + var redirect = e.redirectedConstructor; + if (redirect == null) return result; + + // Get the raw constructor element before the type is applied. + var rawRedirect = + redirect is a.ConstructorMember ? redirect.baseElement : redirect; + + // TODO(jmesserly): conceptually we only need a reference here, but + // RedirectingFactoryBody requires the complete node. + var ctor = rawRedirect.isFactory + ? _visitFactory(rawRedirect) + : visitConstructorElement(rawRedirect); + + var redirectedType = redirect.type.returnType as a.InterfaceType; + var typeArgs = redirectedType.typeArguments.map(visitType).toList(); + result.function.body = RedirectingFactoryBody(ctor, typeArgs); + return result; + } + + Field _createRedirectingFactoryField( + List factories, a.ClassElement c) { + return Field(_getName(c, "_redirecting#"), + isStatic: true, + initializer: ListLiteral(List.of(factories.map((f) => StaticGet(f)))), + fileUri: c.source.uri); + } + + LibraryDependency visitExportElement(a.ExportElement e) => + LibraryDependency.byReference( + LibraryDependency.ExportFlag, + const [], + _reference(e.exportedLibrary), + null, + e.combinators.map(_visitCombinator).toList()); + + Field visitFieldElement(a.FieldElement e) { + var result = Field(_getName(e), + type: _visitDartType(e.type), + initializer: null, + isFinal: e.isFinal, + isConst: e.isConst, + isStatic: e.isStatic, + fileUri: e.source.uri, + reference: _reference(e)); + if (!e.isFinal && !e.isConst) { + var class_ = e.enclosingElement as a.ClassElement; + if (class_.typeParameters.isNotEmpty) { + result.isGenericCovariantImpl = _isGenericCovariant(class_, e.type); + } + } + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + Procedure visitFunctionElement(a.FunctionElement e) { + var result = Procedure.byReference( + _getName(e), ProcedureKind.Method, _createFunction(e), + isExternal: e.isExternal, + fileUri: e.source.uri, + isStatic: true, + reference: _reference(e)); + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + Typedef visitFunctionTypeAliasElement(a.FunctionTypeAliasElement e, + [Library library]) { + var ref = _reference(e); + if (ref.node != null) return ref.asTypedef; + + var t = Typedef(e.name, null, reference: ref, fileUri: e.source.uri); + library ??= visitLibraryElement(e.library); + library.addTypedef(t); + + a.FunctionType type; + var typeParams = e.typeParameters; + if (e is a.GenericTypeAliasElement) { + type = e.function.type; + } else { + type = e.type; + if (typeParams.isNotEmpty) { + // Skip past the type formals, we'll add them back below, so these + // type parameter names will end up in scope in the generated JS. + type = type.instantiate( + typeParams.map((f) => getLegacyTypeParameterType(f)).toList()); + } + } + t.typeParameters.addAll(typeParams.map(visitTypeParameterElement)); + setParents(t.typeParameters, t); + t.type = _visitDartType(type, originTypedef: t.thisType); + _visitAnnotations(e.metadata, t.addAnnotation); + return t; + } + + LibraryDependency visitImportElement(a.ImportElement e) => + LibraryDependency.byReference(0, const [], _reference(e.importedLibrary), + null, e.combinators.map(_visitCombinator).toList()); + + Library visitLibraryElement(a.LibraryElement e) { + var ref = _reference(e); + if (ref.node != null) return ref.asLibrary; + + var library = Library(e.source.uri, + name: e.name, + fileUri: e.definingCompilationUnit.source.uri, + reference: ref); + library.fileOffset = 0; + + _visitAnnotations(e.metadata, library.addAnnotation); + e.imports.map(visitImportElement).forEach(library.addDependency); + e.exports.map(visitExportElement).forEach(library.addDependency); + e.parts.map((p) => LibraryPart(const [], p.uri)).forEach(library.addPart); + + _visitUnit(a.CompilationUnitElement u) { + for (var t in u.types) { + visitClassElement(t, library); + } + for (var t in u.mixins) { + visitClassElement(t, library); + } + for (var t in u.functionTypeAliases) { + visitFunctionTypeAliasElement(t, library); + } + u.functions.map(visitFunctionElement).forEach(library.addMember); + u.accessors + .where((a) => !a.isSynthetic) + .map(visitPropertyAccessorElement) + .forEach(library.addMember); + u.topLevelVariables + .map(visitTopLevelVariableElement) + .forEach(library.addMember); + } + + _visitUnit(e.definingCompilationUnit); + e.parts.forEach(_visitUnit); + + var libraryImpl = e as a.LibraryElementImpl; + libraryImpl.publicNamespace ??= + _namespaceBuilder.createPublicNamespaceForLibrary(e); + libraryImpl.exportNamespace ??= + _namespaceBuilder.createExportNamespaceForLibrary(e); + var publicNames = libraryImpl.publicNamespace.definedNames; + var exportNames = libraryImpl.exportNamespace.definedNames; + exportNames.forEach((name, value) { + if (!publicNames.containsKey(name)) { + value = value is a.PropertyAccessorElement && value.isSynthetic + ? value.variable + : value; + library.additionalExports.add(_reference(value)); + } + }); + return library; + } + + Procedure visitMethodElement(a.MethodElement e) { + var result = Procedure.byReference( + _getName(e), + e.isOperator ? ProcedureKind.Operator : ProcedureKind.Method, + _createFunction(e), + isAbstract: e.isAbstract, + isStatic: e.isStatic, + isExternal: e.isExternal, + fileUri: e.source.uri, + reference: _reference(e)); + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + Procedure visitPropertyAccessorElement(a.PropertyAccessorElement e) { + var result = Procedure.byReference( + _getName(e, e.variable.name), + e.isGetter ? ProcedureKind.Getter : ProcedureKind.Setter, + _createFunction(e), + isAbstract: e.isAbstract, + isStatic: e.isStatic, + isExternal: e.isExternal, + fileUri: e.source.uri, + reference: _reference(e)); + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + Field visitTopLevelVariableElement(a.TopLevelVariableElement e) { + var result = Field(_getName(e), + type: _visitDartType(e.type), + initializer: null, + isFinal: e.isFinal, + isConst: e.isConst, + isStatic: e.isStatic, + fileUri: e.source.uri, + reference: _reference(e)); + _visitAnnotations(e.metadata, result.addAnnotation); + return result; + } + + TypeParameter visitTypeParameterElement(a.TypeParameterElement e) { + var t = _typeParams[e]; + if (t != null) return t; + _typeParams[e] = t = TypeParameter(e.name); + + var hasBound = e.bound != null; + t.bound = + hasBound ? _visitDartType(e.bound) : _visitDartType(types.objectType); + t.defaultType = hasBound ? t.bound : const DynamicType(); + + var enclosingElement = e.enclosingElement; + if (hasBound && enclosingElement is a.ClassMemberElement) { + var class_ = enclosingElement.enclosingElement; + if (class_ is a.ClassElement && class_.typeParameters.isNotEmpty) { + t.isGenericCovariantImpl = _isGenericCovariant(class_, e.bound); + } + } + return t; + } + + Name _getName(a.Element e, [String name]) { + name ??= e.name; + return Name.byReference( + name, name.startsWith('_') ? _reference(e.library) : null); + } + + /// Converts an Analyzer [type] to a Kernel type. + /// + /// If [ensureNode] is set, the reference to the [Class] or [Typedef] will + /// populated with the node (creating it if needed). Many members on + /// [InterfaceType] and [TypedefType] rely on having a node present, so this + /// enables the use of those members if they're needed by the converter. + DartType _visitDartType(a.DartType type, + {bool ensureNode = false, TypedefType originTypedef}) { + if (type.isVoid) { + return const VoidType(); + } else if (type.isDynamic) { + return const DynamicType(); + } else if (type.isBottom) { + return const BottomType(); + } else if (type is a.TypeParameterType) { + return TypeParameterType(visitTypeParameterElement(type.element)); + } + + visit(a.DartType t) => _visitDartType(t, ensureNode: ensureNode); + + if (type is a.InterfaceType) { + var ref = ensureNode + ? visitClassElement(type.element).reference + : _reference(type.element); + var typeArgs = type.typeArguments; + var newTypeArgs = typeArgs.isNotEmpty + ? typeArgs.map(visit).toList() + : const []; + return InterfaceType.byReference(ref, newTypeArgs); + } + + var f = type as a.FunctionType; + if (f.name != null && f.name != '') { + var ref = ensureNode + ? visitFunctionTypeAliasElement( + f.element as a.FunctionTypeAliasElement) + .reference + : _reference(f.element); + return TypedefType.byReference(ref, f.typeArguments.map(visit).toList()); + } + var params = f.parameters; + var positional = f.normalParameterTypes.map(visit).toList(); + positional.addAll(f.optionalParameterTypes.map(visit)); + + var named = []; + f.namedParameterTypes.forEach((name, type) { + named.add(NamedType(name, visit(type))); + }); + + return FunctionType(positional, visit(f.returnType), + typeParameters: f.typeFormals.map(visitTypeParameterElement).toList(), + namedParameters: named, + requiredParameterCount: params.where((p) => !p.isOptional).length, + typedefType: originTypedef); + } + + Supertype _typeToSupertype(a.InterfaceType t) { + if (t == null) return null; + return Supertype( + visitClassElement(t.element), + t.typeArguments + .map((a) => _visitDartType(a, ensureNode: true)) + .toList()); + } + + Combinator _visitCombinator(a.NamespaceCombinator combinator) { + bool isShow; + List names; + if (combinator is a.ShowElementCombinator) { + isShow = true; + names = combinator.shownNames; + } else { + isShow = false; + names = (combinator as a.HideElementCombinator).hiddenNames; + } + return Combinator(isShow, names); + } + + /// Creates a function node for the executable element [e], optionally using + /// the supplied [typeParameters] and calling [visitType] so it can perform + /// any necessary substitutions. + FunctionNode _createFunction(a.ExecutableElement e, + [List typeParameters, + DartType Function(a.DartType) visitType]) { + visitType ??= _visitDartType; + + var enclosingElement = e.enclosingElement; + var class_ = enclosingElement is a.ClassElement ? enclosingElement : null; + + visitParameter(a.ParameterElement e) { + var result = VariableDeclaration(e.name, + type: visitType(e.type), + isFinal: e.isFinal, + isFieldFormal: e.isInitializingFormal, + isCovariant: e.isCovariant, + initializer: + e.isOptional ? _visitConstant(e.computeConstantValue()) : null); + if (class_ != null && class_.typeParameters.isNotEmpty) { + result.isGenericCovariantImpl = _isGenericCovariant(class_, e.type); + } + return result; + } + + var params = e.parameters; + var asyncMarker = _getAsyncMarker(e); + return FunctionNode(null, + typeParameters: typeParameters ?? + e.typeParameters.map(visitTypeParameterElement).toList(), + positionalParameters: + params.where((p) => !p.isNamed).map(visitParameter).toList(), + namedParameters: + params.where((p) => p.isNamed).map(visitParameter).toList(), + requiredParameterCount: params.where((p) => !p.isOptional).length, + returnType: visitType(e.returnType), + asyncMarker: asyncMarker, + dartAsyncMarker: asyncMarker); + } + + Reference _reference(a.Element e) { + if (e == null) throw ArgumentError('null element'); + return _references.putIfAbsent(e, () => Reference()); + } + + bool _isGenericCovariant(a.ClassElement c, a.DartType type) { + var classUpperBound = + rules.instantiateToBounds(getLegacyRawClassType(c)) as a.InterfaceType; + var typeUpperBound = type.substitute2(classUpperBound.typeArguments, + a.TypeParameterTypeImpl.getTypes(classUpperBound.typeParameters)); + // Is it safe to assign the upper bound of the field/parameter to it? + // If not then we'll need a runtime check. + return !rules.isSubtypeOf(typeUpperBound, type); + } + + /// Transforms a metadata annotation from Analyzer to Kernel format. + /// + /// If needed this uses Analyzer's constant evaluation to evaluate the AST, + /// and then converts the resulting constant value into a Kernel tree. + /// By first computing the expression's constant value, we avoid having to + /// convert a bunch of Analyzer ASTs nodes. Instead we can convert the more + /// limited set of constant values allowed in Dart (see [_visitConstant]). + void _visitAnnotations(List metadata, + void Function(Expression) addAnnotation) { + if (metadata.isEmpty) return; + + for (a.ElementAnnotation annotation in metadata) { + var ast = (annotation as a.ElementAnnotationImpl).annotationAst; + var arguments = ast.arguments; + if (arguments == null) { + var e = ast.element; + e = e is a.PropertyAccessorElement && e.isSynthetic ? e.variable : e; + addAnnotation(StaticGet.byReference(_reference(e))); + } else { + // Use Analyzer's constant evaluation to produce the constant, then + // emit the resulting value. We do this to avoid handling all of the + // AST nodes that might be needed for constant evaluation. Instead we + // just serialize the resulting value to a Kernel expression that will + // reproduce it. + addAnnotation(_visitConstant(annotation.computeConstantValue())); + } + } + } + + /// Converts an Analyzer constant value in [obj] to a Kernel expression + /// (usually a Literal or ConstructorInvocation) that will recreate that + /// constant value. + Expression _visitConstant(a.DartObject obj) { + if (obj == null || obj.isNull || !obj.hasKnownValue) return NullLiteral(); + + var type = obj.type; + if (identical(type, types.boolType)) { + var value = obj.toBoolValue(); + return value != null ? BoolLiteral(value) : NullLiteral(); + } + if (identical(type, types.intType)) { + return IntLiteral(obj.toIntValue()); + } + if (identical(type, types.doubleType)) { + return DoubleLiteral(obj.toDoubleValue()); + } + if (identical(type, types.stringType)) { + return StringLiteral(obj.toStringValue()); + } + if (identical(type, types.symbolType)) { + return SymbolLiteral(obj.toSymbolValue()); + } + if (identical(type, types.typeType)) { + return TypeLiteral(_visitDartType(obj.toTypeValue())); + } + if (type is a.InterfaceType) { + if (type.element == types.listElement) { + return ListLiteral(obj.toListValue().map(_visitConstant).toList(), + typeArgument: _visitDartType(type.typeArguments[0]), isConst: true); + } + if (type.element == types.mapElement) { + var entries = obj + .toMapValue() + .entries + .map( + (e) => MapEntry(_visitConstant(e.key), _visitConstant(e.value))) + .toList(); + return MapLiteral(entries, + keyType: _visitDartType(type.typeArguments[0]), + valueType: _visitDartType(type.typeArguments[1]), + isConst: true); + } + if (obj is a.DartObjectImpl && obj.isUserDefinedObject) { + var classElem = type.element; + if (classElem.isEnum) { + // TODO(jmesserly): we should be able to use `getField('index')` but + // in some cases Analyzer uses the name of the static field that + // contains the enum, rather than the `index` field, due to a bug. + // + // So we just grab the one instance field, regardless of its name. + var index = obj.fields.values.single.toIntValue(); + var field = + classElem.fields.where((f) => f.type == type).elementAt(index); + return StaticGet.byReference(_reference(field)); + } + var invocation = obj.getInvocation(); + var constructor = invocation.constructor; + // For a redirecting const factory, the constant constructor will be + // from the original one, but the `type` will match the redirected type. + // + // This leads to mismatch in how we call this constructor. So we need to + // find the redirected one. + for (a.ConstructorElement rc; + (rc = constructor.redirectedConstructor) != null;) { + constructor = rc; + } + constructor = constructor is a.ConstructorMember + ? constructor.baseElement + : constructor; + return ConstructorInvocation.byReference( + _reference(constructor), + Arguments( + invocation.positionalArguments.map(_visitConstant).toList(), + named: invocation.namedArguments.entries + .map((e) => NamedExpression(e.key, _visitConstant(e.value))) + .toList(), + types: type.typeArguments.map(_visitDartType).toList()), + isConst: true); + } + } + if (obj is a.DartObjectImpl && type is a.FunctionType) { + var e = obj.toFunctionValue(); + e = e is a.PropertyAccessorElement && e.isSynthetic + ? e.variable as a.ExecutableElement + : e; + // TODO(jmesserly): support generic tear-off implicit instantiation. + return StaticGet.byReference(_reference(e)); + } + throw UnsupportedError('unknown constant type `$type`: $obj'); + } +} + +AsyncMarker _getAsyncMarker(a.ExecutableElement e) { + return e.isGenerator + ? (e.isAsynchronous ? AsyncMarker.AsyncStar : AsyncMarker.SyncStar) + : (e.isAsynchronous ? AsyncMarker.Async : AsyncMarker.Sync); +} + +a.LinkedElementFactory _createSummaryResynthesizer( + a.SummaryDataStore summaryData, String dartSdkPath) { + var context = _createContextForSummaries(summaryData, dartSdkPath); + + var elementFactory = a.LinkedElementFactory( + context, + null, + a.Reference.root(), + ); + + for (var bundle in summaryData.bundles) { + elementFactory.addBundle( + a.LinkedBundleContext(elementFactory, bundle.bundle2), + ); + } + + return elementFactory; +} + +/// Creates a dummy Analyzer context so we can use summary resynthesizer. +/// +/// This is similar to Analyzer's `LibraryContext._createResynthesizingContext`. +a.AnalysisContextImpl _createContextForSummaries( + a.SummaryDataStore summaryData, String dartSdkPath) { + var sdk = a.SummaryBasedDartSdk(dartSdkPath, true, + resourceProvider: a.PhysicalResourceProvider.INSTANCE); + var sdkSummaryBundle = sdk.getLinkedBundle(); + if (sdkSummaryBundle != null) { + summaryData.addBundle(null, sdkSummaryBundle); + } + + // TODO(jmesserly): use RestrictedAnalysisContext. + var context = a.AnalysisEngine.instance.createAnalysisContext() + as a.AnalysisContextImpl; + context.sourceFactory = a.SourceFactory( + [a.DartUriResolver(sdk), a.InSummaryUriResolver(null, summaryData)]); + // TODO(jmesserly): do we need to set analysisOptions or declaredVariables? + return context; +} diff --git a/pkg/dev_compiler/lib/src/kernel/command.dart b/pkg/dev_compiler/lib/src/kernel/command.dart index 9a92ecd838c..8401522be61 100644 --- a/pkg/dev_compiler/lib/src/kernel/command.dart +++ b/pkg/dev_compiler/lib/src/kernel/command.dart @@ -25,6 +25,7 @@ import '../js_ast/js_ast.dart' as js_ast; import '../js_ast/js_ast.dart' show js; import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext; +import 'analyzer_to_kernel.dart'; import 'compiler.dart'; import 'target.dart'; @@ -178,17 +179,14 @@ Future _compile(List args, var summaryPaths = options.summaryModules.keys.toList(); var summaryModules = Map.fromIterables( summaryPaths.map(sourcePathToUri), options.summaryModules.values); + var useAnalyzer = summaryPaths.any((s) => !s.endsWith('.dill')); var sdkSummaryPath = argResults['dart-sdk-summary'] as String; var librarySpecPath = argResults['libraries-file'] as String; if (sdkSummaryPath == null) { - sdkSummaryPath = defaultSdkSummaryPath; + sdkSummaryPath = + useAnalyzer ? defaultAnalyzerSdkSummaryPath : defaultSdkSummaryPath; librarySpecPath ??= defaultLibrarySpecPath; } - var invalidSummary = summaryPaths.any((s) => !s.endsWith('.dill')) || - !sdkSummaryPath.endsWith('.dill'); - if (invalidSummary) { - throw StateError("Non-dill file detected in input: $summaryPaths"); - } if (librarySpecPath == null) { // TODO(jmesserly): the `isSupported` bit should be included in the SDK @@ -207,12 +205,14 @@ Future _compile(List args, } } + useAnalyzer = useAnalyzer || !sdkSummaryPath.endsWith('.dill'); + /// The .packages file path provided by the user. // // TODO(jmesserly): the default location is based on the current working // directory, to match the behavior of dartanalyzer/dartdevc. However the // Dart VM, CFE (and dart2js?) use the script file location instead. The - // difference may be due to the lack of a single entry point for Analyzer. + // difference may be due to the lack of a single entry point for DDC/Analyzer. // Ultimately this is just the default behavior; in practice users call DDC // through a build tool, which generally passes in `--packages=`. // @@ -244,7 +244,7 @@ Future _compile(List args, fe.WorkerInputComponent cachedSdkInput; bool recordUsedInputs = argResults['used-inputs-file'] != null; List inputSummaries = summaryModules.keys.toList(); - if (!useIncrementalCompiler) { + if (useAnalyzer || !useIncrementalCompiler) { compilerState = await fe.initializeCompiler( oldCompilerState, compileSdk, @@ -306,8 +306,15 @@ Future _compile(List args, // `initializeCompiler`. Also we should be able to pass down Components for // SDK and summaries. // + if (useAnalyzer && !identical(oldCompilerState, compilerState)) { + var opts = compilerState.processedOpts; + var converter = AnalyzerToKernel(sdkSummaryPath, summaryPaths); + opts.sdkSummaryComponent = converter.convertSdk(); + opts.inputSummariesComponents = converter.convertSummaries(); + } + fe.DdcResult result; - if (!useIncrementalCompiler) { + if (useAnalyzer || !useIncrementalCompiler) { result = await fe.compile(compilerState, inputs, diagnosticMessageHandler); } else { compilerState.options.onDiagnostic = diagnosticMessageHandler; @@ -404,7 +411,7 @@ Future _compile(List args, if (recordUsedInputs) { Set usedOutlines = Set(); - if (useIncrementalCompiler) { + if (!useAnalyzer && useIncrementalCompiler) { compilerState.incrementalCompiler .updateNeededDillLibrariesWithHierarchy(result.classHierarchy, null); for (Library lib @@ -530,6 +537,9 @@ final defaultSdkSummaryPath = final defaultLibrarySpecPath = p.join(getSdkPath(), 'lib', 'libraries.json'); +final defaultAnalyzerSdkSummaryPath = + p.join(getSdkPath(), 'lib', '_internal', 'ddc_sdk.sum'); + bool _checkForDartMirrorsImport(Component component) { for (var library in component.libraries) { if (library.isExternal || library.importUri.scheme == 'dart') continue; diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index d2020b3a964..0939193e0a2 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -45,7 +45,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor /// corresponding Kernel summary module we imported it with. final _importToSummary = Map.identity(); - /// Maps a Kernel summary to the JS import name for the module. + /// Maps a summary to the JS import name for the module. final _summaryToModule = Map.identity(); /// The variable for the current catch clause diff --git a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart index 784e8a39812..45efb1cf1dd 100644 --- a/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart +++ b/pkg/dev_compiler/lib/src/kernel/kernel_helpers.dart @@ -252,7 +252,8 @@ Iterable getRedirectingFactories(Field f) { /// /// This is used to ignore synthetic mixin application classes. /// -// TODO(jmesserly): consider replacing this with Kernel's mixin unrolling +// TODO(jmesserly): consider replacing this with Kernel's mixin unrolling once +// we don't have the Analyzer backend to maintain. Class getSuperclassAndMixins(Class c, List mixins) { assert(mixins.isEmpty); diff --git a/pkg/dev_compiler/web/index.html b/pkg/dev_compiler/web/index.html new file mode 100644 index 00000000000..891bd1996c0 --- /dev/null +++ b/pkg/dev_compiler/web/index.html @@ -0,0 +1,10 @@ + + + + + Dart Dev Compiler Console + + + + + diff --git a/pkg/dev_compiler/web/main.dart b/pkg/dev_compiler/web/main.dart new file mode 100755 index 00000000000..b1267cbd418 --- /dev/null +++ b/pkg/dev_compiler/web/main.dart @@ -0,0 +1,86 @@ +#!/usr/bin/env dart +// Copyright (c) 2016, 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. + +@JS() +library dev_compiler.web.main; + +import 'dart:async'; + +import 'package:args/command_runner.dart'; +import 'package:js/js.dart'; + +import 'web_command.dart'; + +@JS(r'$setUpDartDevCompilerInBrowser') +external set setUpCompilerInBrowser(Function function); + +Future _setUpCompilerInBrowser; +void main() { + var args = ['compile']; + + // Avoid race condition when users try to call $setUpDartDevCompilerInBrowser + // before it is ready by installing the method immediately and making the body + // of the method async. + setUpCompilerInBrowser = allowInterop((String sdkUrl, + JSMap summaryMap, + Function onCompileReady, + Function onError, + [Function onProgress]) async { + (await _setUpCompilerInBrowser)( + sdkUrl, summaryMap, onCompileReady, onError, onProgress); + }); + _runCommand(args); +} + +/// Runs a single compile command, and returns an exit code. +_runCommand(List args, {MessageHandler messageHandler}) { + try { + // TODO: Remove CommandRunner and args if possible. May run into issues + // with ArgResults or ArgParsers. + var runner = CommandRunner('dartdevc', 'Dart Development Compiler'); + runner.addCommand(WebCompileCommand(messageHandler: messageHandler)); + _setUpCompilerInBrowser = runner.run(args); + } catch (e, s) { + _handleError(e, s, args, messageHandler: messageHandler); + } +} + +/// Handles [error] in a uniform fashion. Calls [messageHandler] with messages. +_handleError(dynamic error, dynamic stackTrace, List args, + {MessageHandler messageHandler}) { + messageHandler ??= print; + + if (error is UsageException) { + // Incorrect usage, input file not found, etc. + messageHandler(error); + } else if (error is CompileErrorException) { + // Code has error(s) and failed to compile. + messageHandler(error); + } else { + // Anything else is likely a compiler bug. + // + // --unsafe-force-compile is a bit of a grey area, but it's nice not to + // crash while compiling + // (of course, output code may crash, if it had errors). + // + messageHandler(""); + messageHandler("We're sorry, you've found a bug in our compiler."); + messageHandler("You can report this bug at:"); + messageHandler( + " https://github.com/dart-lang/sdk/issues/labels/web-dev-compiler"); + messageHandler(""); + messageHandler( + "Please include the information below in your report, along with"); + messageHandler( + "any other information that may help us track it down. Thanks!"); + messageHandler(""); + messageHandler(" dartdevc arguments: " + args.join(' ')); + messageHandler(""); + messageHandler("```"); + messageHandler(error); + messageHandler(stackTrace); + messageHandler("```"); + } +} diff --git a/pkg/dev_compiler/web/web_command.dart b/pkg/dev_compiler/web/web_command.dart new file mode 100644 index 00000000000..e58a36397d6 --- /dev/null +++ b/pkg/dev_compiler/web/web_command.dart @@ -0,0 +1,313 @@ +// Copyright (c) 2016, 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. +@JS() +library dev_compiler.web.web_command; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:math' as math; +import 'dart:html' show HttpRequest; +import 'dart:typed_data'; + +import 'package:analyzer/file_system/file_system.dart' show ResourceUriResolver; +import 'package:analyzer/file_system/memory_file_system.dart' + show MemoryResourceProvider; +import 'package:analyzer/src/summary/idl.dart' show PackageBundle; +import 'package:analyzer/src/summary/package_bundle_reader.dart' + show SummaryDataStore; +import 'package:analyzer/src/dart/resolver/scope.dart' show Scope; + +import 'package:args/command_runner.dart'; + +import 'package:dev_compiler/src/analyzer/context.dart' show AnalyzerOptions; +import 'package:dev_compiler/src/analyzer/command.dart'; +import 'package:dev_compiler/src/analyzer/driver.dart'; +import 'package:dev_compiler/src/analyzer/module_compiler.dart'; + +import 'package:dev_compiler/src/compiler/module_builder.dart'; +import 'package:js/js.dart'; +import 'package:path/path.dart' as p; + +typedef void MessageHandler(Object message); + +@JS() +@anonymous +class JSIterator {} + +@JS('Map') +class JSMap { + external V get(K v); + external set(K k, V v); + external JSIterator keys(); + external JSIterator values(); + external int get size; +} + +@JS('Array.from') +external List iteratorToList(JSIterator iterator); + +@JS() +@anonymous +class CompileResult { + external factory CompileResult( + {String code, List errors, bool isValid}); +} + +typedef CompileModule(String imports, String body, String libraryName, + String existingLibrary, String fileName); + +/// The command for invoking the modular compiler. +class WebCompileCommand extends Command { + @override + get name => 'compile'; + + @override + get description => 'Compile a set of Dart files into a JavaScript module.'; + final MessageHandler messageHandler; + + WebCompileCommand({MessageHandler messageHandler}) + : this.messageHandler = messageHandler ?? print { + ddcArgParser(argParser: argParser, help: false); + } + + @override + Function run() { + return requestSummaries; + } + + Future requestSummaries(String sdkUrl, JSMap summaryMap, + Function onCompileReady, Function onError, Function onProgress) async { + var sdkRequest; + var progress = 0; + // Add 1 to the count for the SDK summary. + var total = summaryMap.size + 1; + // No need to report after every summary is loaded. Posting about 100 + // progress updates should be more than sufficient for users to understand + // how long loading will take. + num progressDelta = math.max(total / 100, 1); + num nextProgressToReport = 0; + maybeReportProgress() { + if (nextProgressToReport > progress && progress != total) return; + nextProgressToReport += progressDelta; + if (onProgress != null) onProgress(progress, total); + } + + try { + sdkRequest = await HttpRequest.request(sdkUrl, + responseType: "arraybuffer", + mimeType: "application/octet-stream", + withCredentials: true); + } catch (error) { + onError('Dart sdk summaries failed to load: $error. url: $sdkUrl'); + return null; + } + progress++; + maybeReportProgress(); + + var sdkBytes = (sdkRequest.response as ByteBuffer).asUint8List(); + + // Map summary URLs to HttpRequests. + + var summaryRequests = + iteratorToList(summaryMap.values()).map((String summaryUrl) async { + var request = await HttpRequest.request(summaryUrl, + responseType: "arraybuffer", mimeType: "application/octet-stream"); + progress++; + maybeReportProgress(); + return request; + }).toList(); + try { + var summaryResponses = await Future.wait(summaryRequests); + // Map summary responses to summary bytes. + List> summaryBytes = summaryResponses + .map((response) => (response.response as ByteBuffer).asUint8List()) + .toList(); + onCompileReady(setUpCompile( + sdkBytes, summaryBytes, iteratorToList(summaryMap.keys()))); + } catch (error) { + onError('Summaries failed to load: $error'); + } + } + + List setUpCompile(List sdkBytes, List> summaryBytes, + List moduleIds) { + var dartSdkSummaryPath = '/dart-sdk/lib/_internal/web_sdk.sum'; + + var resources = MemoryResourceProvider() + ..newFileWithBytes(dartSdkSummaryPath, sdkBytes); + + var options = AnalyzerOptions.basic( + dartSdkPath: '/dart-sdk', dartSdkSummaryPath: dartSdkSummaryPath); + + var summaryData = SummaryDataStore([], resourceProvider: resources); + var compilerOptions = CompilerOptions.fromArguments(argResults); + compilerOptions.replCompile = true; + compilerOptions.libraryRoot = '/'; + for (var i = 0; i < summaryBytes.length; i++) { + var bytes = summaryBytes[i]; + + // Packages with no dart source files will have empty invalid summaries. + if (bytes.isEmpty) continue; + + var moduleId = moduleIds[i]; + var url = '/$moduleId.api.ds'; + summaryData.addBundle(url, PackageBundle.fromBuffer(bytes)); + compilerOptions.summaryModules[url] = moduleId; + } + options.analysisRoot = '/web-compile-root'; + options.fileResolvers = [ResourceUriResolver(resources)]; + options.resourceProvider = resources; + + var driver = CompilerAnalysisDriver(options, summaryData: summaryData); + + var resolveFn = (String url) { + var packagePrefix = 'package:'; + var uri = Uri.parse(url); + var base = p.basename(url); + var parts = uri.pathSegments; + var match; + int bestScore = 0; + for (var candidate in summaryData.uriToSummaryPath.keys) { + if (p.basename(candidate) != base) continue; + List candidateParts = p.dirname(candidate).split('/'); + var first = candidateParts.first; + + // Process and strip "package:" prefix. + if (first.startsWith(packagePrefix)) { + first = first.substring(packagePrefix.length); + candidateParts[0] = first; + // Handle convention that directory foo/bar/baz is given package name + // foo.bar.baz + if (first.contains('.')) { + candidateParts = (first.split('.'))..addAll(candidateParts.skip(1)); + } + } + + // If file name and extension don't match... give up. + int i = parts.length - 1; + int j = candidateParts.length - 1; + + int score = 1; + // Greedy algorithm finding matching path segments from right to left + // skipping segments on the candidate path unless the target path + // segment is named lib. + while (i >= 0 && j >= 0) { + if (parts[i] == candidateParts[j]) { + i--; + j--; + score++; + if (j == 0 && i == 0) { + // Arbitrary bonus if we matched all parts of the input + // and used up all parts of the output. + score += 10; + } + } else { + // skip unmatched lib directories from the input + // otherwise skip unmatched parts of the candidate. + if (parts[i] == 'lib') { + i--; + } else { + j--; + } + } + } + + if (score > bestScore) { + match = candidate; + } + } + return match; + }; + + CompileModule compileFn = (String imports, String body, String libraryName, + String existingLibrary, String fileName) { + // Instead of returning a single function, return a pair of functions. + // Create a new virtual File that contains the given Dart source. + String sourceCode; + if (existingLibrary == null) { + sourceCode = imports + body; + } else { + var dir = p.dirname(existingLibrary); + // Need to pull in all the imports from the existing library and + // re-export all privates as privates in this library. + // Assumption: summaries are available for all libraries, including any + // source files that were compiled; we do not need to reconstruct any + // summary data here. + var unlinked = driver.summaryData.unlinkedMap[existingLibrary]; + if (unlinked == null) { + throw "Unable to get library element for `$existingLibrary`."; + } + var sb = StringBuffer(imports); + sb.write('\n'); + + // TODO(jacobr): we need to add a proper Analyzer flag specifing that + // cross-library privates should be in scope instead of this hack. + // We set the private name prefix for scope resolution to an invalid + // character code so that the analyzer ignores normal Dart private + // scoping rules for top level names allowing REPL users to access + // privates in arbitrary libraries. The downside of this scheme is it is + // possible to get errors if privates in the current library and + // imported libraries happen to have exactly the same name. + Scope.PRIVATE_NAME_PREFIX = -1; + + // We emulate running code in the context of an existing library by + // importing that library and all libraries it imports. + sb.write('import ${json.encode(existingLibrary)};\n'); + + for (var import in unlinked.imports) { + if (import.uri == null || import.isImplicit) continue; + var uri = import.uri; + // dart: and package: uris are not relative but the path package + // thinks they are. We have to provide absolute uris as our library + // has a different directory than the library we are pretending to be. + if (p.isRelative(uri) && + !uri.startsWith('package:') && + !uri.startsWith('dart:')) { + uri = p.normalize(p.join(dir, uri)); + } + sb.write('import ${json.encode(uri)}'); + if (import.prefixReference != 0) { + var prefix = unlinked.references[import.prefixReference].name; + sb.write(' as $prefix'); + } + for (var combinator in import.combinators) { + if (combinator.shows.isNotEmpty) { + sb.write(' show ${combinator.shows.join(', ')}'); + } else if (combinator.hides.isNotEmpty) { + sb.write(' hide ${combinator.hides.join(', ')}'); + } else { + throw 'Unexpected element combinator'; + } + } + sb.write(';\n'); + } + sb.write(body); + sourceCode = sb.toString(); + } + resources.newFile(fileName, sourceCode); + + var name = p.toUri(libraryName).toString(); + compilerOptions.moduleName = name; + JSModuleFile module = + compileWithAnalyzer(driver, [fileName], options, compilerOptions); + + var moduleCode = ''; + if (module.isValid) { + moduleCode = + module.getCode(ModuleFormat.legacyConcat, name, name + '.map').code; + } + + return CompileResult( + code: moduleCode, isValid: module.isValid, errors: module.errors); + }; + + return [allowInterop(compileFn), allowInterop(resolveFn)]; + } +} + +/// Thrown when the input source code has errors. +class CompileErrorException implements Exception { + @override + toString() => '\nPlease fix all errors before compiling (warnings are okay).'; +} diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index d80314ae8d4..9a4a1c59387 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -785,7 +785,10 @@ copy("copy_dev_compiler_js_es6_kernel") { # Copies all of the JS artifacts needed by DDC. group("copy_dev_compiler_js") { - visibility = [ ":copy_dev_compiler_sdk" ] + visibility = [ + ":copy_dev_compiler_sdk", + ":copy_dev_compiler_tools", + ] public_deps = [ ":copy_dev_compiler_js_amd", ":copy_dev_compiler_js_amd_kernel", @@ -796,12 +799,31 @@ group("copy_dev_compiler_js") { ] } +# This rule copies tools to go along with ddc. +copy("copy_dev_compiler_tools") { + visibility = [ ":copy_dev_compiler_sdk" ] + deps = [ + ":copy_dev_compiler_js", + "../utils/dartdevc:dartdevc_web", + "../utils/dartdevc:stack_trace_mapper", + ] + dart_out = get_label_info("../utils/dartdevc:dartdevc_web", "root_out_dir") + sources = [ + "$dart_out/dev_compiler/build/web/dart_stack_trace_mapper.js", + "$dart_out/dev_compiler/build/web/ddc_web_compiler.js", + ] + outputs = [ + "$root_out_dir/dart-sdk/lib/dev_compiler/web/{{source_file_part}}", + ] +} + # This is the main rule for copying ddc's dependencies to lib/ group("copy_dev_compiler_sdk") { visibility = [ ":create_full_sdk" ] public_deps = [ ":copy_dev_compiler_js", ":copy_dev_compiler_summary", + ":copy_dev_compiler_tools", ] } diff --git a/sdk_nnbd/BUILD.gn b/sdk_nnbd/BUILD.gn index 0dc60fa22c9..6d960001cd8 100644 --- a/sdk_nnbd/BUILD.gn +++ b/sdk_nnbd/BUILD.gn @@ -353,7 +353,6 @@ if (target_os != current_os && target_os == "fuchsia") { # will not find the actual binary. action("copy_dart_nnbd") { visibility = [ ":create_common_sdk_nnbd" ] - # TODO(rnystrom): This probably needs to be forked for NNBD. dart_label = "../runtime/bin:dart" deps = [ @@ -806,14 +805,35 @@ copy("copy_dev_compiler_js_es6_kernel_nnbd") { # Copies all of the JS artifacts needed by DDC. group("copy_dev_compiler_js_nnbd") { - visibility = [ ":copy_dev_compiler_sdk_nnbd" ] + visibility = [ + ":copy_dev_compiler_sdk_nnbd", + ":copy_dev_compiler_tools_nnbd", + ] public_deps = [ - ":copy_dev_compiler_js_amd_kernel_nnbd", ":copy_dev_compiler_js_amd_nnbd", - ":copy_dev_compiler_js_common_kernel_nnbd", + ":copy_dev_compiler_js_amd_kernel_nnbd", ":copy_dev_compiler_js_common_nnbd", - ":copy_dev_compiler_js_es6_kernel_nnbd", + ":copy_dev_compiler_js_common_kernel_nnbd", ":copy_dev_compiler_js_es6_nnbd", + ":copy_dev_compiler_js_es6_kernel_nnbd", + ] +} + +# This rule copies tools to go along with ddc. +copy("copy_dev_compiler_tools_nnbd") { + visibility = [ ":copy_dev_compiler_sdk_nnbd" ] + deps = [ + ":copy_dev_compiler_js_nnbd", + "../utils/dartdevc:dartdevc_web", + "../utils/dartdevc:stack_trace_mapper", + ] + dart_out = get_label_info("../utils/dartdevc:dartdevc_web", "root_out_dir") + sources = [ + "$dart_out/dev_compiler/build/web/dart_stack_trace_mapper.js", + "$dart_out/dev_compiler/build/web/ddc_web_compiler.js", + ] + outputs = [ + "$root_out_dir/dart-sdk-nnbd/lib/dev_compiler/web/{{source_file_part}}", ] } @@ -823,6 +843,7 @@ group("copy_dev_compiler_sdk_nnbd") { public_deps = [ ":copy_dev_compiler_js_nnbd", ":copy_dev_compiler_summary_nnbd", + ":copy_dev_compiler_tools_nnbd", ] } @@ -1000,8 +1021,8 @@ group("create_common_sdk_nnbd") { public_deps = [ ":copy_analysis_summaries_nnbd", ":copy_api_readme_nnbd", - ":copy_dart2native_nnbd", ":copy_dart_nnbd", + ":copy_dart2native_nnbd", ":copy_dartdoc_files_nnbd", ":copy_headers_nnbd", ":copy_libraries_dart_nnbd", @@ -1065,4 +1086,4 @@ group("create_sdk_with_abi_versions_nnbd") { ":copy_abi_dill_files_nnbd", ":create_sdk_nnbd", ] -} +} \ No newline at end of file