mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
Add dart2js noInline
and tryInline
annotations to package:meta
There will be a follow-up change to remove NoInline from package:expect. Change-Id: I6dc33241ea6d8b6a8994da6a8ec5295dab30f4da Reviewed-on: https://dart-review.googlesource.com/17043 Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
347cf98cb5
commit
b3a93f44eb
|
@ -1172,6 +1172,39 @@ class CommonElements {
|
|||
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 the implementation of the 'operator ==' [function] is
|
||||
|
|
|
@ -11,6 +11,10 @@ import '../elements/entities.dart';
|
|||
/// Returns `true` if inlining is disabled for [element].
|
||||
bool noInline(ElementEnvironment elementEnvironment,
|
||||
CommonElements 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.
|
||||
|
@ -20,6 +24,16 @@ bool noInline(ElementEnvironment elementEnvironment,
|
|||
elementEnvironment, element, commonElements.noInlineClass);
|
||||
}
|
||||
|
||||
/// Returns `true` if inlining is requested for [element].
|
||||
bool tryInline(ElementEnvironment elementEnvironment,
|
||||
CommonElements 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(ElementEnvironment elementEnvironment,
|
||||
|
|
|
@ -1019,12 +1019,27 @@ class JavaScriptBackend {
|
|||
return;
|
||||
}
|
||||
|
||||
bool hasNoInline = false;
|
||||
bool hasForceInline = false;
|
||||
|
||||
if (element.isFunction || element.isConstructor) {
|
||||
if (optimizerHints.noInline(
|
||||
elementEnvironment, commonElements, element)) {
|
||||
hasNoInline = true;
|
||||
inlineCache.markAsNonInlinable(element);
|
||||
}
|
||||
if (optimizerHints.tryInline(
|
||||
elementEnvironment, commonElements, element)) {
|
||||
hasForceInline = true;
|
||||
if (hasNoInline) {
|
||||
reporter.reportErrorMessage(element, MessageKind.GENERIC,
|
||||
{'text': '@tryInline must not be used with @noInline.'});
|
||||
} else {
|
||||
inlineCache.markAsMustInline(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element.isField) return;
|
||||
FunctionEntity method = element;
|
||||
|
||||
|
@ -1033,8 +1048,7 @@ class JavaScriptBackend {
|
|||
!canLibraryUseNative(library)) {
|
||||
return;
|
||||
}
|
||||
bool hasNoInline = false;
|
||||
bool hasForceInline = false;
|
||||
|
||||
bool hasNoThrows = false;
|
||||
bool hasNoSideEffects = false;
|
||||
for (ConstantValue constantValue
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 1.1.4
|
||||
|
||||
* Add `@dart2js.noInline` and `@dart2js.tryInline` annotations.
|
||||
|
||||
## 1.1.2
|
||||
|
||||
* Rollback SDK constraint update for 2.0.0. No longer needed.
|
||||
|
|
42
pkg/meta/lib/dart2js.dart
Normal file
42
pkg/meta/lib/dart2js.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// 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.
|
||||
|
||||
/// Constants for use in metadata annotations to provide hints to dart2js.
|
||||
|
||||
library meta_dart2js;
|
||||
|
||||
/// An annotation for methods to request that dart2js does not inline the
|
||||
/// method.
|
||||
///
|
||||
/// import 'package:meta/dart2js.dart' as dart2js;
|
||||
///
|
||||
/// @dart2js.noInline
|
||||
/// String text() => 'A String of unusual size';
|
||||
///
|
||||
const _NoInline noInline = const _NoInline();
|
||||
|
||||
/// An annotation for methods method to request that dart2js always inlines the
|
||||
/// method.
|
||||
///
|
||||
/// dart2js will attempt to inline the method regardless of its size. Even with
|
||||
/// this annotation, there are conditions that can prevent dart2js from inlining
|
||||
/// a method, including complex control flow.
|
||||
///
|
||||
/// import 'package:meta/dart2js.dart' as dart2js;
|
||||
///
|
||||
/// @dart2js.tryInline
|
||||
/// String bigMethod() {
|
||||
/// for (int i in "Hello".runes) print(i);
|
||||
/// }
|
||||
///
|
||||
/// It is an error to use both `@noInline` and `@tryInline`.
|
||||
const _TryInline tryInline = const _TryInline();
|
||||
|
||||
class _NoInline {
|
||||
const _NoInline();
|
||||
}
|
||||
|
||||
class _TryInline {
|
||||
const _TryInline();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
name: meta
|
||||
version: 1.1.3
|
||||
version: 1.1.4
|
||||
author: Dart Team <misc@dartlang.org>
|
||||
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/meta
|
||||
description: >
|
||||
|
|
66
tests/compiler/dart2js/meta_annotations2_test.dart
Normal file
66
tests/compiler/dart2js/meta_annotations2_test.dart
Normal file
|
@ -0,0 +1,66 @@
|
|||
// 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.
|
||||
|
||||
// Functional test of 'noInline' annotation from package:meta/dart2js.dart.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'package:compiler/compiler_new.dart';
|
||||
import 'memory_compiler.dart';
|
||||
|
||||
const MEMORY_SOURCE_FILES = const {
|
||||
'main.dart': r'''
|
||||
import 'package:meta/dart2js.dart';
|
||||
|
||||
@noInline
|
||||
foo(y) => 49912344 + y;
|
||||
|
||||
class A {
|
||||
var field;
|
||||
|
||||
@noInline
|
||||
A([this.field = 4711]);
|
||||
|
||||
@noInline
|
||||
static bar(x) => x + 123455;
|
||||
|
||||
@noInline
|
||||
gee(x, y) => x + y + 81234512;
|
||||
}
|
||||
|
||||
main() {
|
||||
print(foo(23412));
|
||||
print(A.bar(87654));
|
||||
print(new A().gee(1337, 919182));
|
||||
print(new A().field + 1);
|
||||
}'''
|
||||
};
|
||||
|
||||
void main() {
|
||||
asyncTest(() async {
|
||||
OutputCollector collector = new OutputCollector();
|
||||
await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES, outputProvider: collector);
|
||||
// Simply check that the constants of the small functions are still in the
|
||||
// output, and that we don't see the result of constant folding.
|
||||
String jsOutput = collector.getOutput('', OutputType.js);
|
||||
|
||||
void has(String text) {
|
||||
Expect.isTrue(jsOutput.contains(text), "output should contain '$text'");
|
||||
}
|
||||
|
||||
void hasNot(String text) {
|
||||
Expect.isFalse(
|
||||
jsOutput.contains(text), "output must not contain '$text'");
|
||||
}
|
||||
|
||||
has('49912344');
|
||||
has('123455');
|
||||
has('81234512');
|
||||
hasNot('49935756');
|
||||
hasNot('211109');
|
||||
hasNot('82155031');
|
||||
hasNot('4712');
|
||||
});
|
||||
}
|
81
tests/compiler/dart2js/meta_annotations3_test.dart
Normal file
81
tests/compiler/dart2js/meta_annotations3_test.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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.
|
||||
|
||||
// Functional test of 'tryInline' annotation from package:meta/dart2js.dart.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'package:compiler/compiler_new.dart';
|
||||
import 'memory_compiler.dart';
|
||||
|
||||
const MEMORY_SOURCE_FILES = const {
|
||||
'main.dart': r'''
|
||||
import 'package:meta/dart2js.dart';
|
||||
|
||||
monster1(u) {
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
}
|
||||
|
||||
@tryInline
|
||||
monster2(u) {
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
print(u + 1000);
|
||||
}
|
||||
|
||||
main() {
|
||||
// large function used twice should not normally be inlined.
|
||||
monster1(38000992);
|
||||
monster1(38000993);
|
||||
monster2(48000992);
|
||||
monster2(48000993);
|
||||
}'''
|
||||
};
|
||||
|
||||
void main() {
|
||||
asyncTest(() async {
|
||||
OutputCollector collector = new OutputCollector();
|
||||
await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES, outputProvider: collector);
|
||||
String jsOutput = collector.getOutput('', OutputType.js);
|
||||
|
||||
void has(String text) {
|
||||
Expect.isTrue(jsOutput.contains(text), "output should contain '$text'");
|
||||
}
|
||||
|
||||
void hasNot(String text) {
|
||||
print(jsOutput);
|
||||
Expect.isFalse(
|
||||
jsOutput.contains(text), "output must not contain '$text'");
|
||||
}
|
||||
|
||||
// Check that (u + 1000) from monster1 is not inlined and constant folded.
|
||||
has('38000992');
|
||||
has('38000993');
|
||||
hasNot('38001992');
|
||||
hasNot('38001993');
|
||||
|
||||
// Check that (u + 1000) from monster2 is inlined and constant folded.
|
||||
hasNot('48000992');
|
||||
hasNot('48000993');
|
||||
has('48001992');
|
||||
has('48001993');
|
||||
});
|
||||
}
|
69
tests/compiler/dart2js/meta_annotations_test.dart
Normal file
69
tests/compiler/dart2js/meta_annotations_test.dart
Normal file
|
@ -0,0 +1,69 @@
|
|||
// 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.
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/elements/elements.dart';
|
||||
import 'package:compiler/src/js_backend/annotations.dart' as optimizerHints;
|
||||
import 'package:compiler/src/world.dart' show ClosedWorld;
|
||||
import 'memory_compiler.dart';
|
||||
|
||||
const Map MEMORY_SOURCE_FILES = const {
|
||||
'main.dart': r"""
|
||||
import 'package:meta/dart2js.dart';
|
||||
|
||||
int method(String arg) => arg.length;
|
||||
|
||||
@noInline
|
||||
int methodNoInline(String arg) => arg.length;
|
||||
|
||||
@tryInline
|
||||
int methodTryInline(String arg) => arg.length;
|
||||
|
||||
|
||||
void main(List<String> args) {
|
||||
print(method(args[0]));
|
||||
print(methodNoInline('bar'));
|
||||
print(methodTryInline('bar'));
|
||||
}
|
||||
"""
|
||||
};
|
||||
|
||||
main() {
|
||||
asyncTest(() async {
|
||||
CompilationResult result =
|
||||
await runCompiler(memorySourceFiles: MEMORY_SOURCE_FILES);
|
||||
Compiler compiler = result.compiler;
|
||||
ClosedWorld closedWorld =
|
||||
compiler.resolutionWorldBuilder.closedWorldForTesting;
|
||||
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}) {
|
||||
LibraryElement mainApp =
|
||||
compiler.frontendStrategy.elementEnvironment.mainLibrary;
|
||||
MethodElement method = mainApp.find(name);
|
||||
Expect.isNotNull(method);
|
||||
Expect.equals(
|
||||
expectNoInline,
|
||||
optimizerHints.noInline(closedWorld.elementEnvironment,
|
||||
closedWorld.commonElements, method),
|
||||
"Unexpected annotation of @noInline on '$method'.");
|
||||
Expect.equals(
|
||||
expectTryInline,
|
||||
optimizerHints.tryInline(closedWorld.elementEnvironment,
|
||||
closedWorld.commonElements, method),
|
||||
"Unexpected annotation of @tryInline on '$method'.");
|
||||
}
|
||||
|
||||
test('method');
|
||||
test('methodNoInline', expectNoInline: true);
|
||||
test('methodTryInline', expectTryInline: true);
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue