Revert "[dart2js] Simplify noInline / tryInline annotations"

This reverts commit 8bae3a1cfd.

There are versioning issues that need to be addressed.

TBR=kevmoo@google.com

Change-Id: Ia82e94f127523db650e4df03af1179ef94463bee
Reviewed-on: https://dart-review.googlesource.com/76564
Reviewed-by: Stephen Adams <sra@google.com>
Reviewed-by: Kevin Moore <kevmoo@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
Stephen Adams 2018-09-25 22:11:42 +00:00 committed by commit-bot@chromium.org
parent 2c82a400e8
commit ded73cdd08
4 changed files with 147 additions and 100 deletions

View file

@ -502,14 +502,14 @@ abstract class KCommonElements implements CommonElements {
ClassEntity get forceInlineClass;
ClassEntity get pragmaClass;
FieldEntity get pragmaClassNameField;
FieldEntity get pragmaClassOptionsField;
bool isCreateInvocationMirrorHelper(MemberEntity member);
bool isSymbolValidatedConstructor(ConstructorEntity element);
ClassEntity get metaNoInlineClass;
ClassEntity get metaTryInlineClass;
/// Returns `true` if [function] is allowed to be external.
///
/// This returns `true` for foreign helpers, from environment constructors and
@ -1321,18 +1321,6 @@ class CommonElementsImpl
ClassEntity get forceInlineClass =>
_forceInlineClass ??= _findHelperClass('ForceInline');
ClassEntity _pragmaClass;
ClassEntity get pragmaClass =>
_pragmaClass ??= _findClass(coreLibrary, 'pragma');
FieldEntity _pragmaClassNameField;
FieldEntity get pragmaClassNameField =>
_pragmaClassNameField ??= _findClassMember(pragmaClass, 'name');
FieldEntity _pragmaClassOptionsField;
FieldEntity get pragmaClassOptionsField =>
_pragmaClassOptionsField ??= _findClassMember(pragmaClass, 'options');
ClassEntity _jsInvocationMirrorClass;
ClassEntity get jsInvocationMirrorClass =>
_jsInvocationMirrorClass ??= _findHelperClass('JSInvocationMirror');
@ -1710,6 +1698,39 @@ class CommonElementsImpl
return _expectAssumeDynamicClass;
}
static final Uri PACKAGE_META_DART2JS =
new Uri(scheme: 'package', path: 'meta/dart2js.dart');
bool _metaAnnotationChecked = false;
ClassEntity _metaNoInlineClass;
ClassEntity _metaTryInlineClass;
void _ensureMetaAnnotations() {
if (!_metaAnnotationChecked) {
_metaAnnotationChecked = true;
LibraryEntity library = _env.lookupLibrary(PACKAGE_META_DART2JS);
if (library != null) {
_metaNoInlineClass = _env.lookupClass(library, '_NoInline');
_metaTryInlineClass = _env.lookupClass(library, '_TryInline');
if (_metaNoInlineClass == null || _metaTryInlineClass == null) {
// This is not the package you're looking for.
_metaNoInlineClass = null;
_metaTryInlineClass = null;
}
}
}
}
ClassEntity get metaNoInlineClass {
_ensureMetaAnnotations();
return _metaNoInlineClass;
}
ClassEntity get metaTryInlineClass {
_ensureMetaAnnotations();
return _metaTryInlineClass;
}
bool isForeign(MemberEntity element) => element.library == foreignLibrary;
/// Returns `true` if [member] is a "foreign helper", that is, a member whose

View file

@ -11,6 +11,34 @@ import '../diagnostics/messages.dart';
import '../elements/entities.dart';
import '../native/native.dart' as native;
const VERBOSE_OPTIMIZER_HINTS = false;
/// Returns `true` if inlining is disabled for [element].
bool _noInline(KElementEnvironment elementEnvironment,
KCommonElements commonElements, MemberEntity element) {
if (_hasAnnotation(
elementEnvironment, element, commonElements.metaNoInlineClass)) {
return true;
}
if (_hasAnnotation(
elementEnvironment, element, commonElements.expectNoInlineClass)) {
// TODO(floitsch): restrict to elements from the test directory.
return true;
}
return _hasAnnotation(
elementEnvironment, element, commonElements.noInlineClass);
}
/// Returns `true` if inlining is requested for [element].
bool _tryInline(KElementEnvironment elementEnvironment,
KCommonElements commonElements, MemberEntity element) {
if (_hasAnnotation(
elementEnvironment, element, commonElements.metaTryInlineClass)) {
return true;
}
return false;
}
/// Returns `true` if parameter and returns types should be trusted for
/// [element].
bool _trustTypeAnnotations(KElementEnvironment elementEnvironment,
@ -52,7 +80,7 @@ AnnotationsData processAnnotations(
void processMemberAnnotations(MemberEntity element) {
bool hasNoInline = false;
bool hasTryInline = false;
bool hasForceInline = false;
if (_trustTypeAnnotations(elementEnvironment, commonElements, element)) {
annotationsDataBuilder.registerTrustTypeAnnotations(element);
@ -62,107 +90,93 @@ AnnotationsData processAnnotations(
annotationsDataBuilder.registerAssumeDynamic(element);
}
// TODO(sra): Check for inappropriate annotations on fields.
if (element.isField) return;
if (element.isFunction || element.isConstructor) {
if (_noInline(elementEnvironment, commonElements, element)) {
hasNoInline = true;
annotationsDataBuilder.markAsNonInlinable(element);
}
if (_tryInline(elementEnvironment, commonElements, element)) {
hasForceInline = true;
if (hasNoInline) {
reporter.reportErrorMessage(element, MessageKind.GENERIC,
{'text': '@tryInline must not be used with @noInline.'});
} else {
annotationsDataBuilder.markAsTryInline(element);
}
}
}
if (element.isField) return;
FunctionEntity method = element;
LibraryEntity library = element.library;
bool platformAnnotationsAllowed = library.canonicalUri.scheme == 'dart' ||
native.maybeEnableNative(library.canonicalUri);
LibraryEntity library = method.library;
if (library.canonicalUri.scheme != 'dart' &&
!native.maybeEnableNative(library.canonicalUri)) {
return;
}
bool hasNoThrows = false;
bool hasNoSideEffects = false;
for (ConstantValue constantValue
in elementEnvironment.getMemberMetadata(method)) {
if (!constantValue.isConstructedObject) continue;
ConstructedConstantValue value = constantValue;
ObjectConstantValue value = constantValue;
ClassEntity cls = value.type.element;
if (platformAnnotationsAllowed) {
if (cls == commonElements.forceInlineClass) {
hasTryInline = true;
} else if (cls == commonElements.noInlineClass) {
hasNoInline = true;
} else if (cls == commonElements.noThrowsClass) {
hasNoThrows = true;
bool isValid = true;
if (method.isTopLevel) {
isValid = true;
} else if (method.isStatic) {
isValid = true;
} else if (method is ConstructorEntity &&
method.isFactoryConstructor) {
isValid = true;
}
if (!isValid) {
reporter.internalError(
method,
"@NoThrows() is currently limited to top-level"
" or static functions and factory constructors.");
}
annotationsDataBuilder.registerCannotThrow(method);
} else if (cls == commonElements.noSideEffectsClass) {
hasNoSideEffects = true;
annotationsDataBuilder.registerSideEffectsFree(method);
if (cls == commonElements.forceInlineClass) {
hasForceInline = true;
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
method, MessageKind.GENERIC, {'text': "Must inline"});
}
}
if (cls == commonElements.expectNoInlineClass) {
annotationsDataBuilder.markAsTryInline(method);
} else if (cls == commonElements.noInlineClass) {
hasNoInline = true;
} else if (cls == commonElements.pragmaClass) {
// Recognize:
//
// @pragma('dart2js:noInline')
// @pragma('dart2js:tryInline')
//
ConstantValue nameValue =
value.fields[commonElements.pragmaClassNameField];
if (nameValue == null || !nameValue.isString) continue;
String name = (nameValue as StringConstantValue).stringValue;
if (!name.startsWith('dart2js:')) continue;
ConstantValue optionsValue =
value.fields[commonElements.pragmaClassOptionsField];
if (name == 'dart2js:noInline') {
if (!optionsValue.isNull) {
reporter.reportErrorMessage(element, MessageKind.GENERIC,
{'text': "@pragma('$name') annotation does not take options"});
}
hasNoInline = true;
} else if (name == 'dart2js:tryInline') {
if (!optionsValue.isNull) {
reporter.reportErrorMessage(element, MessageKind.GENERIC,
{'text': "@pragma('$name') annotation does not take options"});
}
hasTryInline = true;
} else if (!platformAnnotationsAllowed) {
reporter.reportErrorMessage(element, MessageKind.GENERIC,
{'text': "Unknown dart2js pragma @pragma('$name')"});
} else {
// Handle platform-only `@pragma` annotations.
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
method, MessageKind.GENERIC, {'text': "Cannot inline"});
}
annotationsDataBuilder.markAsNonInlinable(method);
} else if (cls == commonElements.noThrowsClass) {
hasNoThrows = true;
bool isValid = true;
if (method.isTopLevel) {
isValid = true;
} else if (method.isStatic) {
isValid = true;
} else if (method is ConstructorEntity && method.isFactoryConstructor) {
isValid = true;
}
if (!isValid) {
reporter.internalError(
method,
"@NoThrows() is currently limited to top-level"
" or static functions and factory constructors.");
}
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
method, MessageKind.GENERIC, {'text': "Cannot throw"});
}
annotationsDataBuilder.registerCannotThrow(method);
} else if (cls == commonElements.noSideEffectsClass) {
hasNoSideEffects = true;
if (VERBOSE_OPTIMIZER_HINTS) {
reporter.reportHintMessage(
method, MessageKind.GENERIC, {'text': "Has no side effects"});
}
annotationsDataBuilder.registerSideEffectsFree(method);
}
}
if (hasTryInline && hasNoInline) {
reporter.reportErrorMessage(element, MessageKind.GENERIC,
{'text': '@tryInline must not be used with @noInline.'});
hasTryInline = false;
}
if (hasNoInline) {
annotationsDataBuilder.markAsNonInlinable(method);
}
if (hasTryInline) {
annotationsDataBuilder.markAsTryInline(method);
if (hasForceInline && hasNoInline) {
reporter.internalError(
method, "@ForceInline() must not be used with @NoInline.");
}
if (hasNoThrows && !hasNoInline) {
reporter.internalError(
method, "@NoThrows() should always be combined with @noInline.");
method, "@NoThrows() should always be combined with @NoInline.");
}
if (hasNoSideEffects && !hasNoInline) {
reporter.internalError(
method, "@NoSideEffects() should always be combined with @noInline.");
method, "@NoSideEffects() should always be combined with @NoInline.");
}
}

View file

@ -17,7 +17,7 @@ library meta_dart2js;
///
/// @dart2js.noInline
/// String text() => 'A String of unusual size';
const pragma noInline = const pragma('dart2js:noInline');
const _NoInline noInline = const _NoInline();
/// An annotation for methods method to request that dart2js always inlines the
/// method.
@ -34,4 +34,12 @@ const pragma noInline = const pragma('dart2js:noInline');
/// }
///
/// It is an error to use both `@noInline` and `@tryInline` on the same method.
const pragma tryInline = const pragma('dart2js:tryInline');
const _TryInline tryInline = const _TryInline();
class _NoInline {
const _NoInline();
}
class _TryInline {
const _TryInline();
}

View file

@ -40,6 +40,10 @@ main() {
compiler.resolutionWorldBuilder.closedWorldForTesting;
KElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
Expect.isFalse(compiler.compilationFailed, 'Unsuccessful compilation');
Expect.isNotNull(closedWorld.commonElements.metaNoInlineClass,
'NoInlineClass is unresolved.');
Expect.isNotNull(closedWorld.commonElements.metaTryInlineClass,
'TryInlineClass is unresolved.');
void test(String name,
{bool expectNoInline: false, bool expectTryInline: false}) {