blob: df7624c5f399c4258b6b4ad0c72d1396a9d0460d [file] [log] [blame]
// Copyright (c) 2017, 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.
// @dart=2.12
// TODO(johnniwinther): Add a test that ensure that this library doesn't depend
// on the dart2js internals.
library compiler.src.kernel.dart2js_target;
import 'package:_fe_analyzer_shared/src/messages/codes.dart'
show Message, LocatedMessage;
import 'package:_js_interop_checks/js_interop_checks.dart';
import 'package:_js_interop_checks/src/transformations/js_util_optimizer.dart';
import 'package:_js_interop_checks/src/transformations/static_interop_class_eraser.dart';
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/reference_from_index.dart';
import 'package:kernel/target/changed_structure_notifier.dart';
import 'package:kernel/target/targets.dart';
import '../options.dart';
import 'invocation_mirror_constants.dart';
import 'transformations/clone_mixin_methods_with_super.dart' as transformMixins
show transformLibraries;
import 'transformations/lowering.dart' as lowering show transformLibraries;
const Iterable<String> _allowedDartSchemePaths = [
'async',
'html',
'html_common',
'indexed_db',
'js',
'js_util',
'svg',
'_native_typed_data',
'web_audio',
'web_gl',
];
List<Pattern> _allowedNativeTestPatterns = [
RegExp(r'(?<!generated_)tests/web/native'),
RegExp(r'(?<!generated_)tests/web/internal'),
'generated_tests/web/native/native_test',
'generated_tests/web/internal/deferred_url_test',
RegExp(r'(?<!generated_)tests/web_2/native'),
RegExp(r'(?<!generated_)tests/web_2/internal'),
'generated_tests/web_2/native/native_test',
'generated_tests/web_2/internal/deferred_url_test',
];
bool allowedNativeTest(Uri uri) {
String path = uri.path;
return _allowedNativeTestPatterns.any((pattern) => path.contains(pattern));
}
bool maybeEnableNative(Uri uri) {
bool allowedDartLibrary() {
if (!uri.isScheme('dart')) return false;
return _allowedDartSchemePaths.contains(uri.path);
}
return allowedNativeTest(uri) || allowedDartLibrary();
}
/// A kernel [Target] to configure the Dart Front End for dart2js.
class Dart2jsTarget extends Target {
@override
final TargetFlags flags;
@override
final String name;
final CompilerOptions? options;
final bool canPerformGlobalTransforms;
final bool supportsUnevaluatedConstants;
Dart2jsTarget(this.name, this.flags,
{this.options,
this.canPerformGlobalTransforms = true,
this.supportsUnevaluatedConstants = true});
@override
bool get enableNoSuchMethodForwarders => true;
@override
int get enabledLateLowerings => LateLowering.none;
@override
bool get supportsLateLoweringSentinel => true;
@override
bool get useStaticFieldLowering => false;
// TODO(johnniwinther,sigmund): Remove this when js-interop handles getter
// calls encoded with an explicit property get or disallows getter calls.
@override
bool get supportsExplicitGetterCalls => false;
@override
int get enabledConstructorTearOffLowerings => ConstructorTearOffLowering.all;
@override
List<String> get extraRequiredLibraries => requiredLibraries[name]!;
@override
List<String> get extraIndexedLibraries => const [
'dart:_foreign_helper',
'dart:_interceptors',
'dart:_js_helper',
'dart:_late_helper',
'dart:js',
'dart:js_util'
];
@override
bool mayDefineRestrictedType(Uri uri) =>
uri.isScheme('dart') &&
(uri.path == 'core' ||
uri.path == 'typed_data' ||
uri.path == '_interceptors' ||
uri.path == '_native_typed_data');
@override
bool allowPlatformPrivateLibraryAccess(Uri importer, Uri imported) =>
super.allowPlatformPrivateLibraryAccess(importer, imported) ||
maybeEnableNative(importer) ||
(importer.isScheme('package') &&
importer.path.startsWith('dart2js_runtime_metrics/'));
@override
bool enableNative(Uri uri) => maybeEnableNative(uri);
@override
bool get nativeExtensionExpectsString => false;
@override
bool get errorOnUnexactWebIntLiterals => true;
@override
void performOutlineTransformations(
ir.Component component, CoreTypes coreTypes) {
component
.accept(StaticInteropStubCreator(StaticInteropClassEraser(coreTypes)));
}
@override
void performModularTransformationsOnLibraries(
ir.Component component,
CoreTypes coreTypes,
ClassHierarchy hierarchy,
List<ir.Library> libraries,
Map<String, String>? environmentDefines,
DiagnosticReporter diagnosticReporter,
ReferenceFromIndex? referenceFromIndex,
{void Function(String msg)? logger,
ChangedStructureNotifier? changedStructureNotifier}) {
var nativeClasses = JsInteropChecks.getNativeClasses(component);
var jsUtilOptimizer = JsUtilOptimizer(coreTypes, hierarchy);
var staticInteropClassEraser = StaticInteropClassEraser(coreTypes);
for (var library in libraries) {
JsInteropChecks(
coreTypes,
diagnosticReporter as DiagnosticReporter<Message, LocatedMessage>,
nativeClasses)
.visitLibrary(library);
// TODO (rileyporter): Merge js_util optimizations with other lowerings
// in the single pass in `transformations/lowering.dart`.
jsUtilOptimizer.visitLibrary(library);
staticInteropClassEraser.visitLibrary(library);
}
lowering.transformLibraries(libraries, coreTypes, hierarchy, options);
logger?.call("Lowering transformations performed");
if (canPerformGlobalTransforms) {
transformMixins.transformLibraries(libraries);
logger?.call("Mixin transformations performed");
}
}
@override
ir.Expression instantiateInvocation(
CoreTypes coreTypes,
ir.Expression receiver,
String name,
ir.Arguments arguments,
int offset,
bool isSuper) {
int kind;
if (name.startsWith('get:')) {
kind = invocationMirrorGetterKind;
name = name.substring(4);
} else if (name.startsWith('set:')) {
kind = invocationMirrorSetterKind;
name = name.substring(4);
} else {
kind = invocationMirrorMethodKind;
}
return ir.StaticInvocation(
coreTypes.index
.getTopLevelProcedure('dart:core', '_createInvocationMirror'),
ir.Arguments(<ir.Expression>[
ir.StringLiteral(name)..fileOffset = offset,
ir.ListLiteral(arguments.types
.map<ir.Expression>((t) => ir.TypeLiteral(t))
.toList()),
ir.ListLiteral(arguments.positional)..fileOffset = offset,
ir.MapLiteral(List<ir.MapLiteralEntry>.from(
arguments.named.map((ir.NamedExpression arg) {
return ir.MapLiteralEntry(
ir.StringLiteral(arg.name)..fileOffset = arg.fileOffset,
arg.value)
..fileOffset = arg.fileOffset;
})), keyType: coreTypes.stringNonNullableRawType)
..isConst = (arguments.named.length == 0)
..fileOffset = arguments.fileOffset,
ir.IntLiteral(kind)..fileOffset = offset,
]))
..fileOffset = offset;
}
@override
ir.Expression instantiateNoSuchMethodError(CoreTypes coreTypes,
ir.Expression receiver, String name, ir.Arguments arguments, int offset,
{bool isMethod = false,
bool isGetter = false,
bool isSetter = false,
bool isField = false,
bool isLocalVariable = false,
bool isDynamic = false,
bool isSuper = false,
bool isStatic = false,
bool isConstructor = false,
bool isTopLevel = false}) {
// TODO(sigmund): implement;
return ir.InvalidExpression(null);
}
@override
ConstantsBackend get constantsBackend => Dart2jsConstantsBackend(
supportsUnevaluatedConstants: supportsUnevaluatedConstants);
@override
DartLibrarySupport get dartLibrarySupport =>
const Dart2jsDartLibrarySupport();
}
const implicitlyUsedLibraries = <String>[
'dart:_foreign_helper',
'dart:_interceptors',
'dart:_js_helper',
'dart:_late_helper',
'dart:js_util'
];
// TODO(sigmund): this "extraRequiredLibraries" needs to be removed...
// compile-platform should just specify which libraries to compile instead.
const requiredLibraries = <String, List<String>>{
'dart2js': [
'dart:_async_await_error_codes',
'dart:_dart2js_runtime_metrics',
'dart:_foreign_helper',
'dart:_http',
'dart:_interceptors',
'dart:_internal',
'dart:_js',
'dart:_js_annotations',
'dart:_js_embedded_names',
'dart:_js_helper',
'dart:_js_names',
'dart:_js_primitives',
'dart:_js_shared_embedded_names',
'dart:_late_helper',
'dart:_metadata',
'dart:_native_typed_data',
'dart:_recipe_syntax',
'dart:_rti',
'dart:async',
'dart:collection',
'dart:convert',
'dart:developer',
'dart:html',
'dart:html_common',
'dart:indexed_db',
'dart:io',
'dart:isolate',
'dart:js',
'dart:js_util',
'dart:math',
'dart:svg',
'dart:typed_data',
'dart:web_audio',
'dart:web_gl',
],
'dart2js_server': [
'dart:_async_await_error_codes',
'dart:_dart2js_runtime_metrics',
'dart:_foreign_helper',
'dart:_http',
'dart:_interceptors',
'dart:_internal',
'dart:_js',
'dart:_js_annotations',
'dart:_js_embedded_names',
'dart:_js_helper',
'dart:_js_names',
'dart:_js_primitives',
'dart:_js_shared_embedded_names',
'dart:_late_helper',
'dart:_native_typed_data',
'dart:_recipe_syntax',
'dart:_rti',
'dart:async',
'dart:collection',
'dart:convert',
'dart:developer',
'dart:io',
'dart:isolate',
'dart:js',
'dart:js_util',
'dart:math',
'dart:typed_data',
]
};
/// Extends the Dart2jsTarget to transform outlines to meet the requirements
/// of summaries in bazel and package-build.
class Dart2jsSummaryTarget extends Dart2jsTarget with SummaryMixin {
@override
final List<Uri> sources;
@override
final bool excludeNonSources;
Dart2jsSummaryTarget(String name, this.sources, this.excludeNonSources,
TargetFlags targetFlags)
: super(name, targetFlags);
}
class Dart2jsConstantsBackend extends ConstantsBackend {
@override
final bool supportsUnevaluatedConstants;
const Dart2jsConstantsBackend({required this.supportsUnevaluatedConstants});
@override
NumberSemantics get numberSemantics => NumberSemantics.js;
}
class Dart2jsDartLibrarySupport extends CustomizedDartLibrarySupport {
const Dart2jsDartLibrarySupport()
: super(supported: const {'_dart2js_runtime_metrics'});
}