mirror of
https://github.com/dart-lang/sdk
synced 2024-10-07 09:11:55 +00:00
[dart2js] Lower JS_GET_FLAG in phase 0b kernel transformer
Also adds `JS_FALSE` to replace uses of `JS_GET_FLAG(false)`. See the doc comment for details. Change-Id: I33f89f158c1955a19fa299f22c50d51e36ede3d8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330171 Commit-Queue: Mayank Patke <fishythefish@google.com> Reviewed-by: Stephen Adams <sra@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com>
This commit is contained in:
parent
580384a28e
commit
5a0ff4ceb1
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (c) 2023, 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 'package:kernel/ast.dart';
|
||||||
|
import 'package:kernel/core_types.dart';
|
||||||
|
|
||||||
|
import '../../../options.dart';
|
||||||
|
|
||||||
|
/// An AST transformation which lowers invocations of `JS_GET_FLAG`.
|
||||||
|
///
|
||||||
|
/// `JS_GET_FLAG` is defined in `dart:_foreign_helper` and is invoked in runtime
|
||||||
|
/// libraries as a way of accessing compiler options in runtime code. The return
|
||||||
|
/// value is a de facto constant, so the compiler replaces the invocation by its
|
||||||
|
/// result before codegen rather than performing an operation at runtime.
|
||||||
|
///
|
||||||
|
/// The earlier this lowering is performed, the more optimizations are enabled.
|
||||||
|
/// Because `JS_GET_FLAG` invocations are typically used as conditions, SSA is
|
||||||
|
/// able to simplify boolean expressions and eliminate dead branches guarded by
|
||||||
|
/// `JS_GET_FLAG`. However, if we lower `JS_GET_FLAG` during SSA, the dead code
|
||||||
|
/// will still previously have been treated as live. Using a kernel
|
||||||
|
/// transformation early allows us to perform treeshaking of the now-dead
|
||||||
|
/// references as well.
|
||||||
|
///
|
||||||
|
/// This transformation is a global transformation because it needs access to
|
||||||
|
/// [CompilerOptions], which is not available modularly.
|
||||||
|
class JsGetFlagLowering {
|
||||||
|
final CoreTypes _coreTypes;
|
||||||
|
final CompilerOptions _options;
|
||||||
|
|
||||||
|
JsGetFlagLowering(this._coreTypes, this._options);
|
||||||
|
|
||||||
|
TreeNode transformStaticInvocation(StaticInvocation node) {
|
||||||
|
if (node.target != _coreTypes.jsGetFlag) return node;
|
||||||
|
final argument = node.arguments.positional.single;
|
||||||
|
|
||||||
|
String? flag;
|
||||||
|
if (argument is StringLiteral) {
|
||||||
|
flag = argument.value;
|
||||||
|
} else if (argument is ConstantExpression) {
|
||||||
|
final constant = argument.constant;
|
||||||
|
if (constant is StringConstant) {
|
||||||
|
flag = constant.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag == null) _unsupportedFlag(argument);
|
||||||
|
final flagValue = _getFlagValue(flag);
|
||||||
|
if (flagValue == null) _unsupportedFlag(flag);
|
||||||
|
return ConstantExpression(BoolConstant(flagValue))
|
||||||
|
..fileOffset = node.fileOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? _getFlagValue(String flagName) {
|
||||||
|
switch (flagName) {
|
||||||
|
case 'DEV_COMPILER':
|
||||||
|
return false;
|
||||||
|
case 'MINIFIED':
|
||||||
|
return _options.enableMinification;
|
||||||
|
case 'MUST_RETAIN_METADATA':
|
||||||
|
return false;
|
||||||
|
case 'USE_CONTENT_SECURITY_POLICY':
|
||||||
|
return _options.features.useContentSecurityPolicy.isEnabled;
|
||||||
|
case 'VARIANCE':
|
||||||
|
return _options.enableVariance;
|
||||||
|
case 'LEGACY':
|
||||||
|
return _options.useLegacySubtyping;
|
||||||
|
case 'EXTRA_NULL_SAFETY_CHECKS':
|
||||||
|
// TODO(fishythefish): Handle this flag as needed.
|
||||||
|
return false;
|
||||||
|
case 'PRINT_LEGACY_STARS':
|
||||||
|
return _options.printLegacyStars;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Never _unsupportedFlag(Object? flag) =>
|
||||||
|
throw UnsupportedError('Unexpected JS_GET_FLAG argument: $flag');
|
||||||
|
}
|
|
@ -3,19 +3,34 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:kernel/ast.dart';
|
import 'package:kernel/ast.dart';
|
||||||
|
import 'package:kernel/core_types.dart';
|
||||||
|
|
||||||
|
import '../../../options.dart';
|
||||||
import 'clone_mixin_methods_with_super.dart' as transformMixins;
|
import 'clone_mixin_methods_with_super.dart' as transformMixins;
|
||||||
|
import 'js_get_flag_lowering.dart';
|
||||||
|
|
||||||
void transformLibraries(List<Library> libraries) {
|
void transformLibraries(
|
||||||
final transformer = _Lowering();
|
List<Library> libraries, CoreTypes coreTypes, CompilerOptions options) {
|
||||||
|
final transformer = _Lowering(coreTypes, options);
|
||||||
libraries.forEach(transformer.visitLibrary);
|
libraries.forEach(transformer.visitLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Lowering extends Transformer {
|
class _Lowering extends Transformer {
|
||||||
|
final JsGetFlagLowering _jsGetFlagLowering;
|
||||||
|
|
||||||
|
_Lowering(CoreTypes coreTypes, CompilerOptions options)
|
||||||
|
: _jsGetFlagLowering = JsGetFlagLowering(coreTypes, options);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Class visitClass(Class node) {
|
Class visitClass(Class node) {
|
||||||
node.transformChildren(this);
|
node.transformChildren(this);
|
||||||
transformMixins.transformClass(node);
|
transformMixins.transformClass(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TreeNode visitStaticInvocation(StaticInvocation node) {
|
||||||
|
node.transformChildren(this);
|
||||||
|
return _jsGetFlagLowering.transformStaticInvocation(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,12 +124,13 @@ class _LoadFromKernelResult {
|
||||||
// serialized components and components from source.
|
// serialized components and components from source.
|
||||||
void _doTransformsOnKernelLoad(Component component, CompilerOptions options) {
|
void _doTransformsOnKernelLoad(Component component, CompilerOptions options) {
|
||||||
if (options.stage.shouldRunGlobalTransforms) {
|
if (options.stage.shouldRunGlobalTransforms) {
|
||||||
globalTransforms.transformLibraries(component.libraries);
|
ir.CoreTypes coreTypes = ir.CoreTypes(component);
|
||||||
|
globalTransforms.transformLibraries(
|
||||||
|
component.libraries, coreTypes, options);
|
||||||
// referenceFromIndex is only necessary in the case where a module
|
// referenceFromIndex is only necessary in the case where a module
|
||||||
// containing a stub definition is invalidated, and then reloaded, because
|
// containing a stub definition is invalidated, and then reloaded, because
|
||||||
// we need to keep existing references to that stub valid. Here, we have the
|
// we need to keep existing references to that stub valid. Here, we have the
|
||||||
// whole program, and therefore do not need it.
|
// whole program, and therefore do not need it.
|
||||||
ir.CoreTypes coreTypes = ir.CoreTypes(component);
|
|
||||||
StaticInteropClassEraser(coreTypes, null,
|
StaticInteropClassEraser(coreTypes, null,
|
||||||
additionalCoreLibraries: {'_js_types', 'js_interop'})
|
additionalCoreLibraries: {'_js_types', 'js_interop'})
|
||||||
.visitComponent(component);
|
.visitComponent(component);
|
||||||
|
|
|
@ -376,39 +376,6 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
||||||
open(newBlock);
|
open(newBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to implement JS_GET_FLAG.
|
|
||||||
///
|
|
||||||
/// The concrete SSA graph builder will extract a flag parameter from the
|
|
||||||
/// JS_GET_FLAG call and then push a boolean result onto the stack. This
|
|
||||||
/// function provides the boolean value corresponding to the given [flagName].
|
|
||||||
/// If [flagName] is not recognized, this function returns `null` and the
|
|
||||||
/// concrete SSA builder reports an error.
|
|
||||||
bool? _getFlagValue(String flagName) {
|
|
||||||
switch (flagName) {
|
|
||||||
case 'FALSE':
|
|
||||||
return false;
|
|
||||||
case 'DEV_COMPILER':
|
|
||||||
return false;
|
|
||||||
case 'MINIFIED':
|
|
||||||
return options.enableMinification;
|
|
||||||
case 'MUST_RETAIN_METADATA':
|
|
||||||
return false;
|
|
||||||
case 'USE_CONTENT_SECURITY_POLICY':
|
|
||||||
return options.features.useContentSecurityPolicy.isEnabled;
|
|
||||||
case 'VARIANCE':
|
|
||||||
return options.enableVariance;
|
|
||||||
case 'LEGACY':
|
|
||||||
return options.useLegacySubtyping;
|
|
||||||
case 'EXTRA_NULL_SAFETY_CHECKS':
|
|
||||||
// TODO(fishythefish): Handle this flag as needed.
|
|
||||||
return false;
|
|
||||||
case 'PRINT_LEGACY_STARS':
|
|
||||||
return options.printLegacyStars;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticType _getStaticType(ir.Expression node) {
|
StaticType _getStaticType(ir.Expression node) {
|
||||||
// TODO(johnniwinther): Substitute the type by the this type and type
|
// TODO(johnniwinther): Substitute the type by the this type and type
|
||||||
// arguments of the current frame.
|
// arguments of the current frame.
|
||||||
|
@ -4474,8 +4441,8 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
||||||
_handleForeignJsEmbeddedGlobal(invocation);
|
_handleForeignJsEmbeddedGlobal(invocation);
|
||||||
} else if (name == 'JS_BUILTIN') {
|
} else if (name == 'JS_BUILTIN') {
|
||||||
_handleForeignJsBuiltin(invocation);
|
_handleForeignJsBuiltin(invocation);
|
||||||
} else if (name == 'JS_GET_FLAG') {
|
} else if (name == 'JS_FALSE') {
|
||||||
_handleForeignJsGetFlag(invocation);
|
_handleForeignJsFalse(invocation);
|
||||||
} else if (name == 'JS_EFFECT') {
|
} else if (name == 'JS_EFFECT') {
|
||||||
stack.add(graph.addConstantNull(closedWorld));
|
stack.add(graph.addConstantNull(closedWorld));
|
||||||
} else if (name == 'JS_INTERCEPTOR_CONSTANT') {
|
} else if (name == 'JS_INTERCEPTOR_CONSTANT') {
|
||||||
|
@ -4987,24 +4954,9 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleForeignJsGetFlag(ir.StaticInvocation invocation) {
|
void _handleForeignJsFalse(ir.StaticInvocation invocation) {
|
||||||
if (_unexpectedForeignArguments(invocation,
|
_unexpectedForeignArguments(invocation, minPositional: 0, maxPositional: 0);
|
||||||
minPositional: 1, maxPositional: 1)) {
|
stack.add(graph.addConstantBool(false, closedWorld));
|
||||||
stack.add(
|
|
||||||
// Result expected on stack.
|
|
||||||
graph.addConstantBool(false, closedWorld));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String name = _foreignConstantStringArgument(invocation, 0, 'JS_GET_FLAG')!;
|
|
||||||
final value = _getFlagValue(name);
|
|
||||||
if (value == null) {
|
|
||||||
reporter.reportErrorMessage(
|
|
||||||
_elementMap.getSpannable(targetElement, invocation),
|
|
||||||
MessageKind.GENERIC,
|
|
||||||
{'text': 'Error: Unknown internal flag "$name".'});
|
|
||||||
} else {
|
|
||||||
stack.add(graph.addConstantBool(value, closedWorld));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleJsInterceptorConstant(ir.StaticInvocation invocation) {
|
void _handleJsInterceptorConstant(ir.StaticInvocation invocation) {
|
||||||
|
|
|
@ -353,6 +353,9 @@ class CoreTypes {
|
||||||
late final Field enumNameField =
|
late final Field enumNameField =
|
||||||
index.getField('dart:core', '_Enum', '_name');
|
index.getField('dart:core', '_Enum', '_name');
|
||||||
|
|
||||||
|
late final Procedure jsGetFlag =
|
||||||
|
index.getTopLevelProcedure('dart:_foreign_helper', 'JS_GET_FLAG');
|
||||||
|
|
||||||
InterfaceType get objectLegacyRawType {
|
InterfaceType get objectLegacyRawType {
|
||||||
return _objectLegacyRawType ??= _legacyRawTypes[objectClass] ??=
|
return _objectLegacyRawType ??= _legacyRawTypes[objectClass] ??=
|
||||||
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
|
new InterfaceType(objectClass, Nullability.legacy, const <DartType>[]);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import 'dart:_js_helper'
|
||||||
unwrapException;
|
unwrapException;
|
||||||
|
|
||||||
import 'dart:_foreign_helper'
|
import 'dart:_foreign_helper'
|
||||||
show JS, JS_GET_FLAG, JS_RAW_EXCEPTION, RAW_DART_FUNCTION_REF;
|
show JS, JS_FALSE, JS_RAW_EXCEPTION, RAW_DART_FUNCTION_REF;
|
||||||
|
|
||||||
import 'dart:_async_status_codes' as async_status_codes;
|
import 'dart:_async_status_codes' as async_status_codes;
|
||||||
|
|
||||||
|
@ -597,7 +597,7 @@ class _SyncStarIterator<T> implements Iterator<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool moveNext() {
|
bool moveNext() {
|
||||||
if (JS_GET_FLAG('FALSE')) _modelGeneratedCode();
|
if (JS_FALSE()) _modelGeneratedCode();
|
||||||
Object? errorValue;
|
Object? errorValue;
|
||||||
int errorCode = async_status_codes.SUCCESS;
|
int errorCode = async_status_codes.SUCCESS;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
@ -232,6 +232,11 @@ external JS_BUILTIN(String typeDescription, JsBuiltin builtin,
|
||||||
/// when the program has been analyzed.
|
/// when the program has been analyzed.
|
||||||
external bool JS_GET_FLAG(String name);
|
external bool JS_GET_FLAG(String name);
|
||||||
|
|
||||||
|
/// Always returns `false`. Because this method is opaque until SSA, it can be
|
||||||
|
/// used to guard code which should be treated as live but not emitted by
|
||||||
|
/// default.
|
||||||
|
external bool JS_FALSE();
|
||||||
|
|
||||||
/// Returns the underlying JavaScript exception. Must be used in a catch block.
|
/// Returns the underlying JavaScript exception. Must be used in a catch block.
|
||||||
///
|
///
|
||||||
/// try {
|
/// try {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'dart:_js_embedded_names'
|
||||||
show DISPATCH_PROPERTY_NAME, TYPE_TO_INTERCEPTOR_MAP;
|
show DISPATCH_PROPERTY_NAME, TYPE_TO_INTERCEPTOR_MAP;
|
||||||
|
|
||||||
import 'dart:collection' hide LinkedList, LinkedListEntry;
|
import 'dart:collection' hide LinkedList, LinkedListEntry;
|
||||||
import 'dart:_foreign_helper' show JS_GET_FLAG, TYPE_REF;
|
import 'dart:_foreign_helper' show JS_FALSE, JS_GET_FLAG, TYPE_REF;
|
||||||
import 'dart:_internal' hide Symbol;
|
import 'dart:_internal' hide Symbol;
|
||||||
import "dart:_internal" as _symbol_dev show Symbol;
|
import "dart:_internal" as _symbol_dev show Symbol;
|
||||||
import 'dart:_js_helper'
|
import 'dart:_js_helper'
|
||||||
|
|
|
@ -340,7 +340,7 @@ final class JSNumber extends Interceptor implements double {
|
||||||
|
|
||||||
int operator ~/(num other) {
|
int operator ~/(num other) {
|
||||||
if (other is! num) throw argumentErrorValue(other);
|
if (other is! num) throw argumentErrorValue(other);
|
||||||
if (JS_GET_FLAG('FALSE')) _tdivFast(other); // Ensure resolution.
|
if (JS_FALSE()) _tdivFast(other); // Ensure resolution.
|
||||||
if (_isInt32(this)) {
|
if (_isInt32(this)) {
|
||||||
if (other >= 1 || other < -1) {
|
if (other >= 1 || other < -1) {
|
||||||
return JS('int', r'(# / #) | 0', this, other);
|
return JS('int', r'(# / #) | 0', this, other);
|
||||||
|
@ -400,7 +400,7 @@ final class JSNumber extends Interceptor implements double {
|
||||||
num operator >>(num other) {
|
num operator >>(num other) {
|
||||||
if (other is! num) throw argumentErrorValue(other);
|
if (other is! num) throw argumentErrorValue(other);
|
||||||
if (other < 0) throw argumentErrorValue(other);
|
if (other < 0) throw argumentErrorValue(other);
|
||||||
if (JS_GET_FLAG('FALSE')) _shrReceiverPositive(other);
|
if (JS_FALSE()) _shrReceiverPositive(other);
|
||||||
return _shrOtherPositive(other);
|
return _shrOtherPositive(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue