Re-land "[vm/precomp] Use @pragma("vm:exact-result-type") to specify method behavior."

We need to block interrupts while evaluating pragmas to prevert reentrant class finalization.
Original revision is in patchset 0.

Change-Id: I872cec4eaf4ca85567c9657c458ed39c8b2e30de
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-release-x64-try, vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/73160
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
Samir Jindel 2018-09-11 18:14:51 +00:00 committed by commit-bot@chromium.org
parent 2b296a0671
commit cbd17b535f
38 changed files with 615 additions and 1551 deletions

View file

@ -1176,7 +1176,8 @@ class _WorkList {
class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
final TypeEnvironment environment;
final LibraryIndex libraryIndex;
final NativeCodeOracle nativeCodeOracle;
final PragmaAnnotationParser annotationMatcher;
NativeCodeOracle nativeCodeOracle;
_ClassHierarchyCache hierarchyCache;
SummaryCollector summaryCollector;
_InvocationsCache _invocationsCache;
@ -1187,8 +1188,10 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
TypeFlowAnalysis(Component component, CoreTypes coreTypes,
ClosedWorldClassHierarchy hierarchy, this.environment, this.libraryIndex,
{List<String> entryPointsJSONFiles, EntryPointsAnnotationMatcher matcher})
: nativeCodeOracle = new NativeCodeOracle(libraryIndex) {
{List<String> entryPointsJSONFiles, PragmaAnnotationParser matcher})
: annotationMatcher =
matcher ?? new ConstantPragmaAnnotationParser(coreTypes) {
nativeCodeOracle = new NativeCodeOracle(libraryIndex, annotationMatcher);
hierarchyCache = new _ClassHierarchyCache(this, hierarchy);
summaryCollector =
new SummaryCollector(environment, this, nativeCodeOracle);
@ -1199,10 +1202,8 @@ class TypeFlowAnalysis implements EntryPointsListener, CallHandler {
nativeCodeOracle.processEntryPointsJSONFiles(entryPointsJSONFiles, this);
}
matcher ??= new ConstantEntryPointsAnnotationMatcher(coreTypes);
component
.accept(new PragmaEntryPointsVisitor(this, nativeCodeOracle, matcher));
component.accept(new PragmaEntryPointsVisitor(
this, nativeCodeOracle, annotationMatcher));
}
_Invocation get currentInvocation => workList.callStack.last;

File diff suppressed because it is too large Load diff

View file

@ -29,81 +29,123 @@ abstract class EntryPointsListener {
ConcreteType addAllocatedClass(Class c);
}
abstract class ParsedPragma {}
enum PragmaEntryPointType { Always, GetterOnly, SetterOnly }
const kEntryPointPragmaName = "vm:entry-point";
const kNativeMethodPragmaName = "vm:native-method";
abstract class EntryPointsAnnotationMatcher {
PragmaEntryPointType annotationsDefineRoot(List<Expression> annotations);
class ParsedEntryPointPragma extends ParsedPragma {
final PragmaEntryPointType type;
ParsedEntryPointPragma(this.type);
}
class ConstantEntryPointsAnnotationMatcher
implements EntryPointsAnnotationMatcher {
class ParsedResultTypeByTypePragma extends ParsedPragma {
final DartType type;
ParsedResultTypeByTypePragma(this.type);
}
class ParsedResultTypeByPathPragma extends ParsedPragma {
final String path;
ParsedResultTypeByPathPragma(this.path);
}
const kEntryPointPragmaName = "vm:entry-point";
const kExactResultTypePragmaName = "vm:exact-result-type";
abstract class PragmaAnnotationParser {
/// May return 'null' if the annotation does not represent a recognized
/// @pragma.
ParsedPragma parsePragma(Expression annotation);
}
class ConstantPragmaAnnotationParser extends PragmaAnnotationParser {
final CoreTypes coreTypes;
ConstantEntryPointsAnnotationMatcher(this.coreTypes);
ConstantPragmaAnnotationParser(this.coreTypes);
PragmaEntryPointType definesRoot(InstanceConstant constant) {
if (constant.classReference.node != coreTypes.pragmaClass) return null;
ParsedPragma parsePragma(Expression annotation) {
InstanceConstant pragmaConstant;
if (annotation is ConstantExpression) {
Constant constant = annotation.constant;
if (constant is InstanceConstant) {
if (constant.classReference.node == coreTypes.pragmaClass) {
pragmaConstant = constant;
}
}
}
if (pragmaConstant == null) return null;
Constant name = constant.fieldValues[coreTypes.pragmaName.reference];
assertx(name != null);
if (name is! StringConstant ||
(name as StringConstant).value != kEntryPointPragmaName) {
String pragmaName;
Constant name = pragmaConstant.fieldValues[coreTypes.pragmaName.reference];
if (name is StringConstant) {
pragmaName = name.value;
} else {
return null;
}
Constant options = constant.fieldValues[coreTypes.pragmaOptions.reference];
Constant options =
pragmaConstant.fieldValues[coreTypes.pragmaOptions.reference];
assertx(options != null);
if (options is NullConstant) return PragmaEntryPointType.Always;
if (options is BoolConstant && options.value == true) {
return PragmaEntryPointType.Always;
}
if (options is StringConstant) {
if (options.value == "get") {
return PragmaEntryPointType.GetterOnly;
} else if (options.value == "set") {
return PragmaEntryPointType.SetterOnly;
} else {
throw "Error: string directive to @pragma('$kEntryPointPragmaName', ...) must be either 'get' or 'set'.";
}
}
return null;
}
@override
PragmaEntryPointType annotationsDefineRoot(List<Expression> annotations) {
for (var annotation in annotations) {
if (annotation is ConstantExpression) {
Constant constant = annotation.constant;
if (constant is InstanceConstant) {
var type = definesRoot(constant);
if (type != null) return type;
switch (pragmaName) {
case kEntryPointPragmaName:
PragmaEntryPointType type;
if (options is NullConstant) {
type = PragmaEntryPointType.Always;
} else if (options is BoolConstant && options.value == true) {
type = PragmaEntryPointType.Always;
} else if (options is StringConstant) {
if (options.value == "get") {
type = PragmaEntryPointType.GetterOnly;
} else if (options.value == "set") {
type = PragmaEntryPointType.SetterOnly;
} else {
throw "Error: string directive to @pragma('$kEntryPointPragmaName', ...) "
"must be either 'get' or 'set'.";
}
}
} else {
throw "All annotations must be constants!";
}
return type != null ? new ParsedEntryPointPragma(type) : null;
case kExactResultTypePragmaName:
if (options == null) return null;
if (options is TypeLiteralConstant) {
return new ParsedResultTypeByTypePragma(options.type);
} else if (options is StringConstant) {
return new ParsedResultTypeByPathPragma(options.value);
}
throw "ERROR: Unsupported option to '$kExactResultTypePragmaName' "
"pragma: $options";
default:
return null;
}
return null;
}
}
class PragmaEntryPointsVisitor extends RecursiveVisitor {
final EntryPointsListener entryPoints;
final NativeCodeOracle nativeCodeOracle;
final EntryPointsAnnotationMatcher matcher;
final PragmaAnnotationParser matcher;
Class currentClass = null;
PragmaEntryPointsVisitor(
this.entryPoints, this.nativeCodeOracle, this.matcher);
this.entryPoints, this.nativeCodeOracle, this.matcher) {
assertx(matcher != null);
}
PragmaEntryPointType _annotationsDefineRoot(List<Expression> annotations) {
for (var annotation in annotations) {
ParsedPragma pragma = matcher.parsePragma(annotation);
if (pragma == null) continue;
if (pragma is ParsedEntryPointPragma) return pragma.type;
}
return null;
}
@override
visitClass(Class klass) {
var type = matcher.annotationsDefineRoot(klass.annotations);
var type = _annotationsDefineRoot(klass.annotations);
if (type != null) {
if (type != PragmaEntryPointType.Always) {
throw "Error: pragma entry-point definition on a class must evaluate to null, true or false. See entry_points_pragma.md.";
throw "Error: pragma entry-point definition on a class must evaluate "
"to null, true or false. See entry_points_pragma.md.";
}
entryPoints.addAllocatedClass(klass);
}
@ -113,10 +155,12 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
@override
visitProcedure(Procedure proc) {
var type = matcher.annotationsDefineRoot(proc.annotations);
var type = _annotationsDefineRoot(proc.annotations);
if (type != null) {
if (type != PragmaEntryPointType.Always) {
throw "Error: pragma entry-point definition on a procedure (including getters and setters) must evaluate to null, true or false. See entry_points_pragma.md.";
throw "Error: pragma entry-point definition on a procedure (including"
"getters and setters) must evaluate to null, true or false. "
"See entry_points_pragma.md.";
}
var callKind = proc.isGetter
? CallKind.PropertyGet
@ -130,10 +174,11 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
@override
visitConstructor(Constructor ctor) {
var type = matcher.annotationsDefineRoot(ctor.annotations);
var type = _annotationsDefineRoot(ctor.annotations);
if (type != null) {
if (type != PragmaEntryPointType.Always) {
throw "Error: pragma entry-point definition on a constructor must evaluate to null, true or false. See entry_points_pragma.md.";
throw "Error: pragma entry-point definition on a constructor must "
"evaluate to null, true or false. See entry_points_pragma.md.";
}
entryPoints
.addRawCall(new DirectSelector(ctor, callKind: CallKind.Method));
@ -144,7 +189,7 @@ class PragmaEntryPointsVisitor extends RecursiveVisitor {
@override
visitField(Field field) {
var type = matcher.annotationsDefineRoot(field.annotations);
var type = _annotationsDefineRoot(field.annotations);
if (type == null) return;
void addSelector(CallKind ck) {
@ -176,8 +221,11 @@ class NativeCodeOracle {
<String, List<Map<String, dynamic>>>{};
final LibraryIndex _libraryIndex;
final Set<Member> _membersReferencedFromNativeCode = new Set<Member>();
final PragmaAnnotationParser _matcher;
NativeCodeOracle(this._libraryIndex);
NativeCodeOracle(this._libraryIndex, this._matcher) {
assertx(_matcher != null);
}
void setMemberReferencedFromNativeCode(Member member) {
_membersReferencedFromNativeCode.add(member);
@ -195,6 +243,49 @@ class NativeCodeOracle {
final nativeActions = _nativeMethods[nativeName];
for (var annotation in member.annotations) {
ParsedPragma pragma = _matcher.parsePragma(annotation);
if (pragma == null) continue;
if (pragma is ParsedResultTypeByTypePragma ||
pragma is ParsedResultTypeByPathPragma) {
// We can only use the 'vm:exact-result-type' pragma on methods in core
// libraries for safety reasons. See 'result_type_pragma.md', detail 1.2
// for explanation.
if (member.enclosingLibrary.importUri.scheme != "dart") {
throw "ERROR: Cannot use $kExactResultTypePragmaName "
"outside core libraries.";
}
}
if (pragma is ParsedResultTypeByTypePragma) {
var type = pragma.type;
if (type is InterfaceType) {
returnType = entryPointsListener.addAllocatedClass(type.classNode);
break;
}
throw "ERROR: Invalid return type for native method: ${pragma.type}";
} else if (pragma is ParsedResultTypeByPathPragma) {
List<String> parts = pragma.path.split("#");
if (parts.length != 2) {
throw "ERROR: Could not parse native method return type: ${pragma.path}";
}
String libName = parts[0];
String klassName = parts[1];
// Error is thrown on the next line if the class is not found.
Class klass = _libraryIndex.getClass(libName, klassName);
Type concreteClass = entryPointsListener.addAllocatedClass(klass);
returnType = concreteClass;
break;
}
}
if (returnType != null) {
assertx(nativeActions == null || nativeActions.length == 0);
return returnType;
}
if (nativeActions != null) {
for (var action in nativeActions) {
if (action['action'] == 'return') {

View file

@ -1209,7 +1209,7 @@ class CreateAllSummariesVisitor extends RecursiveVisitor<Null> {
CreateAllSummariesVisitor(this._environment)
: _summaryColector = new SummaryCollector(_environment,
new EmptyEntryPointsListener(), new NativeCodeOracle(null));
new EmptyEntryPointsListener(), new NativeCodeOracle(null, null));
@override
defaultMember(Member m) {

View file

@ -33,7 +33,7 @@ const bool kDumpClassHierarchy =
/// Assumes strong mode and closed world.
Component transformComponent(
CoreTypes coreTypes, Component component, List<String> entryPoints,
[EntryPointsAnnotationMatcher matcher]) {
[PragmaAnnotationParser matcher]) {
if ((entryPoints == null) || entryPoints.isEmpty) {
throw 'Error: unable to perform global type flow analysis without entry points.';
}

View file

@ -0,0 +1,64 @@
// 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 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:vm/transformations/type_flow/native_code.dart';
import 'package:vm/transformations/type_flow/utils.dart';
// Since we don't run the constants transformation in this test, we can't
// recognize all pragma annotations precisely. Instead, we pattern match on
// annotations which look like a pragma and assume that their options field
// evaluates to true.
class ExpressionPragmaAnnotationParser extends PragmaAnnotationParser {
final CoreTypes coreTypes;
ExpressionPragmaAnnotationParser(this.coreTypes);
ParsedPragma parsePragma(Expression annotation) {
assertx(annotation is! ConstantExpression);
String pragmaName;
Expression options;
if (annotation is ConstructorInvocation) {
if (annotation.constructedType.classNode != coreTypes.pragmaClass) {
return null;
}
if (annotation.arguments.types.length != 0 ||
(annotation.arguments.positional.length != 1 &&
annotation.arguments.positional.length != 2) ||
annotation.arguments.named.length != 0) {
throw "Cannot evaluate pragma annotation $annotation";
}
var arguments = annotation.arguments.positional;
var nameArg = arguments[0];
if (nameArg is StringLiteral) {
pragmaName = nameArg.value;
} else {
return null;
}
options = arguments.length > 1 ? arguments[1] : new NullLiteral();
}
switch (pragmaName) {
case kEntryPointPragmaName:
// We ignore the option because we can't properly evaluate it, assume
// it's true.
return new ParsedEntryPointPragma(PragmaEntryPointType.Always);
case kExactResultTypePragmaName:
if (options is TypeLiteral) {
return new ParsedResultTypeByTypePragma(options.type);
} else if (options is StringLiteral) {
return new ParsedResultTypeByPathPragma(options.value);
}
throw "Could not recognize option to $kExactResultTypePragmaName";
default:
return null;
}
}
}

View file

@ -11,6 +11,7 @@ import 'package:kernel/type_environment.dart';
import 'package:test/test.dart';
import 'package:vm/transformations/type_flow/native_code.dart';
import 'package:vm/transformations/type_flow/summary_collector.dart';
import 'annotation_matcher.dart';
import '../../common_test_utils.dart';
@ -20,9 +21,12 @@ class PrintSummaries extends RecursiveVisitor<Null> {
final SummaryCollector _summaryColector;
final StringBuffer _buf = new StringBuffer();
PrintSummaries(TypeEnvironment environment)
: _summaryColector = new SummaryCollector(environment,
new EmptyEntryPointsListener(), new NativeCodeOracle(null));
PrintSummaries(TypeEnvironment environment, CoreTypes coreTypes)
: _summaryColector = new SummaryCollector(
environment,
new EmptyEntryPointsListener(),
new NativeCodeOracle(
null, new ExpressionPragmaAnnotationParser(coreTypes)));
String print(TreeNode node) {
visitLibrary(node);
@ -42,11 +46,12 @@ class PrintSummaries extends RecursiveVisitor<Null> {
runTestCase(Uri source) async {
final Component component = await compileTestCaseToKernelProgram(source);
final Library library = component.mainMethod.enclosingLibrary;
final CoreTypes coreTypes = new CoreTypes(component);
final typeEnvironment = new TypeEnvironment(
new CoreTypes(component), new ClassHierarchy(component));
final typeEnvironment =
new TypeEnvironment(coreTypes, new ClassHierarchy(component));
final actual = new PrintSummaries(typeEnvironment).print(library);
final actual = new PrintSummaries(typeEnvironment, coreTypes).print(library);
compareResultWithExpectationsFile(source, actual);
}

View file

@ -8,52 +8,14 @@ import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/kernel.dart';
import 'package:test/test.dart';
import 'package:vm/transformations/type_flow/native_code.dart';
import 'package:vm/transformations/type_flow/transformer.dart'
show transformComponent;
import 'package:vm/transformations/type_flow/utils.dart';
import 'annotation_matcher.dart';
import '../../common_test_utils.dart';
final String pkgVmDir = Platform.script.resolve('../../..').toFilePath();
// Since we don't run the constants transformation in this test, we can't
// recognize all pragma annotations precisely. Instead, we pattern match on
// annotations which look like a pragma and assume that their options field
// evaluates to true.
class ExpressionEntryPointsAnnotationMatcher
extends EntryPointsAnnotationMatcher {
final CoreTypes coreTypes;
ExpressionEntryPointsAnnotationMatcher(this.coreTypes);
bool _looksLikePragma(ConstructorInvocation annotation) {
if (annotation.constructedType.classNode != coreTypes.pragmaClass) {
return false;
}
if (annotation.arguments.types.length != 0 ||
annotation.arguments.positional.length < 1 ||
annotation.arguments.named.length != 0) {
throw "Cannot evaluate pragma annotation $annotation";
}
var argument = annotation.arguments.positional[0];
return argument is StringLiteral && argument.value == kEntryPointPragmaName;
}
@override
PragmaEntryPointType annotationsDefineRoot(List<Expression> annotations) {
for (var annotation in annotations) {
assertx(annotation is! ConstantExpression);
if (annotation is ConstructorInvocation && _looksLikePragma(annotation)) {
return PragmaEntryPointType.Always;
}
}
return null;
}
}
runTestCase(Uri source) async {
Component component = await compileTestCaseToKernelProgram(source);
@ -65,7 +27,7 @@ runTestCase(Uri source) async {
];
component = transformComponent(coreTypes, component, entryPoints,
new ExpressionEntryPointsAnnotationMatcher(coreTypes));
new ExpressionPragmaAnnotationParser(coreTypes));
final actual = kernelLibraryToString(component.mainMethod.enclosingLibrary);

View file

@ -9,9 +9,21 @@ These pragmas are part of the VM's API and are safe for use in external code.
[Defining entry-points into Dart code for an embedder or native methods]
(file://../vm/compiler/aot/entry_points_pragma.md)
## Pragmas for internal use
These pragmas can cause unsound behavior if used incorrectly and therefore are only allowed within the core SDK libraries.
- **vm:exact-result-type**
[Declaring an exact result type of a method]
(file://../vm/compiler/result_type_pragma.md)
## Pragmas for internal testing
These pragmas are used for inspecting or modifying internal VM state and should be used exclusively by SDK tests. They must be enabled with the `--enable-testing-pragmas` flag. The names of these pragmas are prefixed with "testing". Additionally, they are categorized into "safe" and "unsafe" forms: "safe" pragmas should not affect the behavior of the program and can be safely added anywhere. "unsafe" pragmas may change the code's behavior or may cause the VM to crash if used improperly.
These pragmas are used for inspecting or modifying internal VM state and should be used exclusively by SDK tests.
They must be enabled with the `--enable-testing-pragmas` flag.
The names of these pragmas are prefixed with "testing".
Additionally, they are categorized into "safe" and "unsafe" forms: "safe" pragmas should not affect the behavior of the program and can be safely added anywhere, whereas "unsafe" pragmas may change the code's behavior or may cause the VM to crash if used improperly.
- **vm:testing.unsafe.trace-entrypoints-fn**

View file

@ -6,6 +6,7 @@
@pragma("vm:entry-point")
class _List<E> extends FixedLengthListBase<E> {
@pragma("vm:exact-result-type", _List)
factory _List(length) native "List_allocate";
E operator [](int index) native "List_getIndexed";
@ -16,6 +17,7 @@ class _List<E> extends FixedLengthListBase<E> {
void _setIndexed(int index, E value) native "List_setIndexed";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get length native "List_getLength";
List _slice(int start, int count, bool needsTypeArgument) {
@ -134,6 +136,7 @@ class _ImmutableList<E> extends UnmodifiableListBase<E> {
E operator [](int index) native "List_getIndexed";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get length native "List_getLength";
List<E> sublist(int start, [int end]) {

View file

@ -1091,6 +1091,7 @@ class _BigIntImpl implements BigInt {
///
/// Note: This function may be intrinsified. Intrinsics on 64-bit platforms
/// process digit pairs at even indices and returns 2.
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int _mulAdd(
Uint32List xDigits,
int xIndex,
@ -1141,6 +1142,7 @@ class _BigIntImpl implements BigInt {
///
/// Note: This function may be intrinsified. Intrinsics on 64-bit platforms
/// process digit pairs at even indices and returns 2.
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int _sqrAdd(
Uint32List xDigits, int i, Uint32List acculumatorDigits, int used) {
int x = xDigits[i];
@ -1261,6 +1263,7 @@ class _BigIntImpl implements BigInt {
/// Estimate `args[_quotientDigit.._quotientHighDigit] = digits[i-3..i] ~/
/// args[_divisorLowTopDigit.._divisorTopDigit]`.
/// Returns 2.
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int _estimateQuotientDigit(Uint32List args, Uint32List digits, int i) {
// Verify that digit pairs are accessible for 64-bit processing.
assert(digits.length >= 4);
@ -2596,6 +2599,7 @@ class _BigIntMontgomeryReduction implements _BigIntReduction {
// args[_muDigit.._muHighDigit] =
// args[_rhoDigit.._rhoHighDigit] * digits[i..i+1] mod _digitBase^2.
// Returns 2.
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int _mulMod(Uint32List args, Uint32List digits, int i) {
var rhol = args[_rhoDigit] & _BigIntImpl._halfDigitMask;
var rhoh = args[_rhoDigit] >> _BigIntImpl._halfDigitBits;

View file

@ -5,6 +5,7 @@
// part of "internal_patch.dart";
class ClassID {
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int getID(Object value) native "ClassID_getID";
// VM injects class id constants into this class.

View file

@ -5,6 +5,7 @@
// part of "internal_patch.dart";
class ClassID {
@pragma("vm:exact-result-type", "dart:core#_Smi")
static int getID(Object value) native "ClassID_getID";
static final int cidArray = 0;

View file

@ -46,18 +46,23 @@ abstract class _HashFieldBase {
// Base class for VM-internal classes; keep in sync with _HashFieldBase.
abstract class _HashVMBase {
@pragma("vm:exact-result-type", "dart:typed_data#_Uint32List")
Uint32List get _index native "LinkedHashMap_getIndex";
void set _index(Uint32List value) native "LinkedHashMap_setIndex";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get _hashMask native "LinkedHashMap_getHashMask";
void set _hashMask(int value) native "LinkedHashMap_setHashMask";
@pragma("vm:exact-result-type", "dart:core#_List")
List get _data native "LinkedHashMap_getData";
void set _data(List value) native "LinkedHashMap_setData";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get _usedData native "LinkedHashMap_getUsedData";
void set _usedData(int value) native "LinkedHashMap_setUsedData";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get _deletedKeys native "LinkedHashMap_getDeletedKeys";
void set _deletedKeys(int value) native "LinkedHashMap_setDeletedKeys";
}

View file

@ -6,27 +6,34 @@
@pragma("vm:entry-point")
class _Double implements double {
@pragma("vm:exact-result-type", _Double)
factory _Double.fromInteger(int value) native "Double_doubleFromInteger";
int get hashCode native "Double_hashCode";
int get _identityHashCode native "Double_hashCode";
@pragma("vm:exact-result-type", _Double)
double operator +(num other) {
return _add(other.toDouble());
}
@pragma("vm:exact-result-type", _Double)
double _add(double other) native "Double_add";
@pragma("vm:exact-result-type", _Double)
double operator -(num other) {
return _sub(other.toDouble());
}
@pragma("vm:exact-result-type", _Double)
double _sub(double other) native "Double_sub";
@pragma("vm:exact-result-type", _Double)
double operator *(num other) {
return _mul(other.toDouble());
}
@pragma("vm:exact-result-type", _Double)
double _mul(double other) native "Double_mul";
int operator ~/(num other) {
@ -35,16 +42,19 @@ class _Double implements double {
int _trunc_div(double other) native "Double_trunc_div";
@pragma("vm:exact-result-type", _Double)
double operator /(num other) {
return _div(other.toDouble());
}
@pragma("vm:exact-result-type", _Double)
double _div(double other) native "Double_div";
double operator %(num other) {
return _modulo(other.toDouble());
}
@pragma("vm:exact-result-type", _Double)
double _modulo(double other) native "Double_modulo";
double remainder(num other) {
@ -53,27 +63,35 @@ class _Double implements double {
double _remainder(double other) native "Double_remainder";
@pragma("vm:exact-result-type", _Double)
double operator -() native "Double_flipSignBit";
@pragma("vm:exact-result-type", bool)
bool operator ==(Object other) {
return (other is num) && _equal(other.toDouble());
}
bool _equal(double other) native "Double_equal";
bool _equalToInteger(int other) native "Double_equalToInteger";
@pragma("vm:exact-result-type", bool)
bool operator <(num other) {
return other > this;
}
@pragma("vm:exact-result-type", bool)
bool operator >(num other) {
return _greaterThan(other.toDouble());
}
bool _greaterThan(double other) native "Double_greaterThan";
@pragma("vm:exact-result-type", bool)
bool operator >=(num other) {
return (this == other) || (this > other);
}
@pragma("vm:exact-result-type", bool)
bool operator <=(num other) {
return (this == other) || (this < other);
}
@ -86,6 +104,7 @@ class _Double implements double {
return new _Double.fromInteger(other)._sub(this);
}
@pragma("vm:exact-result-type", "dart:core#_Double")
double _mulFromInteger(int other) {
return new _Double.fromInteger(other)._mul(this);
}
@ -105,8 +124,11 @@ class _Double implements double {
bool _greaterThanFromInteger(int other)
native "Double_greaterThanFromInteger";
@pragma("vm:exact-result-type", bool)
bool get isNegative native "Double_getIsNegative";
@pragma("vm:exact-result-type", bool)
bool get isInfinite native "Double_getIsInfinite";
@pragma("vm:exact-result-type", bool)
bool get isNaN native "Double_getIsNaN";
bool get isFinite => !isInfinite && !isNaN; // Can be optimized.
@ -127,9 +149,13 @@ class _Double implements double {
int ceil() => ceilToDouble().toInt();
int truncate() => truncateToDouble().toInt();
@pragma("vm:exact-result-type", _Double)
double roundToDouble() native "Double_round";
@pragma("vm:exact-result-type", _Double)
double floorToDouble() native "Double_floor";
@pragma("vm:exact-result-type", _Double)
double ceilToDouble() native "Double_ceil";
@pragma("vm:exact-result-type", _Double)
double truncateToDouble() native "Double_truncate";
num clamp(num lowerLimit, num upperLimit) {

View file

@ -104,10 +104,13 @@ class _GrowableList<T> extends ListBase<T> {
return new _GrowableList<T>.withData(data);
}
@pragma("vm:exact-result-type", _GrowableList)
factory _GrowableList.withData(_List data) native "GrowableList_allocate";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get _capacity native "GrowableList_getCapacity";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get length native "GrowableList_getLength";
void set length(int new_length) {

View file

@ -5,6 +5,7 @@
// part of "core_patch.dart";
@patch
@pragma("vm:exact-result-type", bool)
bool identical(Object a, Object b) native "Identical_comparison";
@patch

View file

@ -61,25 +61,31 @@ abstract class _IntegerImplementation implements int {
int operator >>(int other) => other._shrFromInteger(this);
int operator <<(int other) => other._shlFromInteger(this);
@pragma("vm:exact-result-type", bool)
bool operator <(num other) {
return other > this;
}
@pragma("vm:exact-result-type", bool)
bool operator >(num other) {
return other._greaterThanFromInteger(this);
}
@pragma("vm:exact-result-type", bool)
bool operator >=(num other) {
return (this == other) || (this > other);
}
@pragma("vm:exact-result-type", bool)
bool operator <=(num other) {
return (this == other) || (this < other);
}
@pragma("vm:exact-result-type", bool)
bool _greaterThanFromInteger(int other)
native "Integer_greaterThanFromInteger";
@pragma("vm:exact-result-type", bool)
bool operator ==(Object other) {
if (other is num) {
return other._equalToInteger(this);
@ -87,6 +93,7 @@ abstract class _IntegerImplementation implements int {
return false;
}
@pragma("vm:exact-result-type", bool)
bool _equalToInteger(int other) native "Integer_equalToInteger";
int abs() {
return this < 0 ? -this : this;
@ -224,6 +231,7 @@ abstract class _IntegerImplementation implements int {
return this;
}
@pragma("vm:exact-result-type", _Double)
double toDouble() {
return new _Double.fromInteger(this);
}
@ -458,11 +466,14 @@ class _Smi extends _IntegerImplementation implements _int64 {
}
int get hashCode => this;
int get _identityHashCode => this;
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator ~() native "Smi_bitNegate";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get bitLength native "Smi_bitLength";
int operator &(int other) => other._bitAndFromSmi(this);
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _bitAndFromSmi(_Smi other) native "Smi_bitAndFromSmi";
/**

View file

@ -69,6 +69,7 @@ final bool is64Bit = _inquireIs64Bit();
bool _inquireIs64Bit() native "Internal_inquireIs64Bit";
@pragma("vm:entry-point")
@pragma("vm:exact-result-type", bool)
bool _classRangeCheck(int cid, int lowerLimit, int upperLimit) {
return cid >= lowerLimit && cid <= upperLimit;
}

View file

@ -83,6 +83,7 @@ num pow(num x, num exponent) {
return _doublePow(x.toDouble(), exponent.toDouble());
}
@pragma("vm:exact-result-type", "dart:core#_Double")
double _doublePow(double base, double exponent) {
if (exponent == 0.0) {
return 1.0; // ECMA-262 15.8.2.13
@ -125,20 +126,28 @@ int _intPow(int base, int exponent) {
}
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double atan2(num a, num b) => _atan2(a.toDouble(), b.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double sin(num radians) => _sin(radians.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double cos(num radians) => _cos(radians.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double tan(num radians) => _tan(radians.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double acos(num x) => _acos(x.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double asin(num x) => _asin(x.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double atan(num x) => _atan(x.toDouble());
@patch
@pragma("vm:exact-result-type", "dart:core#_Double")
double sqrt(num x) => _sqrt(x.toDouble());
@patch
double exp(num x) => _exp(x.toDouble());

View file

@ -4,6 +4,7 @@
// part of "core_patch.dart";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _getHash(obj) native "Object_getHash";
void _setHash(obj, hash) native "Object_setHash";
@ -12,6 +13,7 @@ void _setHash(obj, hash) native "Object_setHash";
class Object {
// The VM has its own implementation of equals.
@patch
@pragma("vm:exact-result-type", bool)
bool operator ==(other) native "Object_equals";
// Helpers used to implement hashCode. If a hashCode is used, we remember it
@ -49,9 +51,11 @@ class Object {
}
@patch
@pragma("vm:exact-result-type", "dart:core#_Type")
Type get runtimeType native "Object_runtimeType";
@pragma("vm:entry-point")
@pragma("vm:exact-result-type", bool)
static bool _haveSameRuntimeType(a, b) native "Object_haveSameRuntimeType";
// Call this function instead of inlining instanceof, thus collecting

View file

@ -95,7 +95,10 @@ abstract class _StringBase implements String {
throw new UnsupportedError("_StringBase can't be instaniated");
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get hashCode native "String_getHashCode";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get _identityHashCode native "String_getHashCode";
bool get _isOneByte {
@ -235,8 +238,10 @@ abstract class _StringBase implements String {
int codeUnitAt(int index); // Implemented in the subclasses.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get length native "String_getLength";
@pragma("vm:exact-result-type", bool)
bool get isEmpty {
return this.length == 0;
}
@ -249,6 +254,7 @@ abstract class _StringBase implements String {
return this;
}
@pragma("vm:exact-result-type", bool)
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
@ -284,6 +290,7 @@ abstract class _StringBase implements String {
return 0;
}
@pragma("vm:exact-result-type", bool)
bool _substringMatches(int start, String other) {
if (other.isEmpty) return true;
final len = other.length;
@ -930,18 +937,22 @@ class _OneByteString extends _StringBase {
"_OneByteString can only be allocated by the VM");
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get hashCode native "String_getHashCode";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int codeUnitAt(int index) native "String_codeUnitAt";
bool _isWhitespace(int codeUnit) {
return _StringBase._isOneByteWhitespace(codeUnit);
}
@pragma("vm:exact-result-type", bool)
bool operator ==(Object other) {
return super == other;
}
@pragma("vm:exact-result-type", _OneByteString)
String _substringUncheckedNative(int startIndex, int endIndex)
native "OneByteString_substringUnchecked";
@ -1200,6 +1211,7 @@ class _OneByteString extends _StringBase {
// Allocates a string of given length, expecting its content to be
// set using _setAt.
@pragma("vm:exact-result-type", _OneByteString)
static _OneByteString _allocate(int length) native "OneByteString_allocate";
static _OneByteString _allocateFromOneByteList(List<int> list, int start,
@ -1241,8 +1253,10 @@ class _TwoByteString extends _StringBase {
return _StringBase._isTwoByteWhitespace(codeUnit);
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int codeUnitAt(int index) native "String_codeUnitAt";
@pragma("vm:exact-result-type", bool)
bool operator ==(Object other) {
return super == other;
}
@ -1259,6 +1273,7 @@ class _ExternalOneByteString extends _StringBase {
return _StringBase._isOneByteWhitespace(codeUnit);
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int codeUnitAt(int index) native "String_codeUnitAt";
bool operator ==(Object other) {
@ -1277,6 +1292,7 @@ class _ExternalTwoByteString extends _StringBase {
return _StringBase._isTwoByteWhitespace(codeUnit);
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int codeUnitAt(int index) native "String_codeUnitAt";
bool operator ==(Object other) {

View file

@ -14,6 +14,7 @@ abstract class _AbstractType implements Type {
// Equivalent of RawType.
@pragma("vm:entry-point")
class _Type extends _AbstractType {
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get hashCode native "Type_getHashCode";
}

View file

@ -2045,19 +2045,24 @@ abstract class _TypedList extends _TypedListBase {
// Methods implementing the collection interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int get length native "TypedData_length";
// Internal utility methods.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _getInt8(int offsetInBytes) native "TypedData_GetInt8";
void _setInt8(int offsetInBytes, int value) native "TypedData_SetInt8";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _getUint8(int offsetInBytes) native "TypedData_GetUint8";
void _setUint8(int offsetInBytes, int value) native "TypedData_SetUint8";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _getInt16(int offsetInBytes) native "TypedData_GetInt16";
void _setInt16(int offsetInBytes, int value) native "TypedData_SetInt16";
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _getUint16(int offsetInBytes) native "TypedData_GetUint16";
void _setUint16(int offsetInBytes, int value) native "TypedData_SetUint16";
@ -2073,18 +2078,22 @@ abstract class _TypedList extends _TypedListBase {
int _getUint64(int offsetInBytes) native "TypedData_GetUint64";
void _setUint64(int offsetInBytes, int value) native "TypedData_SetUint64";
@pragma("vm:exact-result-type", "dart:core#_Double")
double _getFloat32(int offsetInBytes) native "TypedData_GetFloat32";
void _setFloat32(int offsetInBytes, double value)
native "TypedData_SetFloat32";
@pragma("vm:exact-result-type", "dart:core#_Double")
double _getFloat64(int offsetInBytes) native "TypedData_GetFloat64";
void _setFloat64(int offsetInBytes, double value)
native "TypedData_SetFloat64";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 _getFloat32x4(int offsetInBytes) native "TypedData_GetFloat32x4";
void _setFloat32x4(int offsetInBytes, Float32x4 value)
native "TypedData_SetFloat32x4";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 _getInt32x4(int offsetInBytes) native "TypedData_GetInt32x4";
void _setInt32x4(int offsetInBytes, Int32x4 value)
native "TypedData_SetInt32x4";
@ -2114,6 +2123,7 @@ abstract class _TypedList extends _TypedListBase {
@patch
class Int8List {
@patch
@pragma("vm:exact-result-type", _Int8List)
factory Int8List(int length) native "TypedData_Int8Array_new";
@patch
@ -2126,6 +2136,7 @@ class Int8List {
@pragma("vm:entry-point")
class _Int8List extends _TypedList with _IntListMixin implements Int8List {
// Method(s) implementing List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2154,6 +2165,7 @@ class _Int8List extends _TypedList with _IntListMixin implements Int8List {
@patch
class Uint8List {
@patch
@pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
@patch
@ -2166,6 +2178,7 @@ class Uint8List {
@pragma("vm:entry-point")
class _Uint8List extends _TypedList with _IntListMixin implements Uint8List {
// Methods implementing List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2194,6 +2207,7 @@ class _Uint8List extends _TypedList with _IntListMixin implements Uint8List {
@patch
class Uint8ClampedList {
@patch
@pragma("vm:exact-result-type", _Uint8ClampedList)
factory Uint8ClampedList(int length) native "TypedData_Uint8ClampedArray_new";
@patch
@ -2208,6 +2222,7 @@ class _Uint8ClampedList extends _TypedList
with _IntListMixin
implements Uint8ClampedList {
// Methods implementing List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2236,6 +2251,7 @@ class _Uint8ClampedList extends _TypedList
@patch
class Int16List {
@patch
@pragma("vm:exact-result-type", _Int16List)
factory Int16List(int length) native "TypedData_Int16Array_new";
@patch
@ -2248,6 +2264,7 @@ class Int16List {
@pragma("vm:entry-point")
class _Int16List extends _TypedList with _IntListMixin implements Int16List {
// Method(s) implementing List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2295,6 +2312,7 @@ class _Int16List extends _TypedList with _IntListMixin implements Int16List {
@patch
class Uint16List {
@patch
@pragma("vm:exact-result-type", _Uint16List)
factory Uint16List(int length) native "TypedData_Uint16Array_new";
@patch
@ -2307,6 +2325,7 @@ class Uint16List {
@pragma("vm:entry-point")
class _Uint16List extends _TypedList with _IntListMixin implements Uint16List {
// Method(s) implementing the List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2354,6 +2373,7 @@ class _Uint16List extends _TypedList with _IntListMixin implements Uint16List {
@patch
class Int32List {
@patch
@pragma("vm:exact-result-type", _Int32List)
factory Int32List(int length) native "TypedData_Int32Array_new";
@patch
@ -2402,6 +2422,7 @@ class _Int32List extends _TypedList with _IntListMixin implements Int32List {
@patch
class Uint32List {
@patch
@pragma("vm:exact-result-type", _Uint32List)
factory Uint32List(int length) native "TypedData_Uint32Array_new";
@patch
@ -2450,6 +2471,7 @@ class _Uint32List extends _TypedList with _IntListMixin implements Uint32List {
@patch
class Int64List {
@patch
@pragma("vm:exact-result-type", _Int64List)
factory Int64List(int length) native "TypedData_Int64Array_new";
@patch
@ -2498,6 +2520,7 @@ class _Int64List extends _TypedList with _IntListMixin implements Int64List {
@patch
class Uint64List {
@patch
@pragma("vm:exact-result-type", _Uint64List)
factory Uint64List(int length) native "TypedData_Uint64Array_new";
@patch
@ -2546,6 +2569,7 @@ class _Uint64List extends _TypedList with _IntListMixin implements Uint64List {
@patch
class Float32List {
@patch
@pragma("vm:exact-result-type", _Float32List)
factory Float32List(int length) native "TypedData_Float32Array_new";
@patch
@ -2560,6 +2584,7 @@ class _Float32List extends _TypedList
with _DoubleListMixin
implements Float32List {
// Method(s) implementing the List interface.
@pragma("vm:exact-result-type", "dart:core#_Double")
double operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2596,6 +2621,7 @@ class _Float32List extends _TypedList
@patch
class Float64List {
@patch
@pragma("vm:exact-result-type", _Float64List)
factory Float64List(int length) native "TypedData_Float64Array_new";
@patch
@ -2610,6 +2636,7 @@ class _Float64List extends _TypedList
with _DoubleListMixin
implements Float64List {
// Method(s) implementing the List interface.
@pragma("vm:exact-result-type", "dart:core#_Double")
double operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2646,6 +2673,7 @@ class _Float64List extends _TypedList
@patch
class Float32x4List {
@patch
@pragma("vm:exact-result-type", _Float32x4List)
factory Float32x4List(int length) native "TypedData_Float32x4Array_new";
@patch
@ -2659,6 +2687,7 @@ class Float32x4List {
class _Float32x4List extends _TypedList
with _Float32x4ListMixin
implements Float32x4List {
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2695,6 +2724,7 @@ class _Float32x4List extends _TypedList
@patch
class Int32x4List {
@patch
@pragma("vm:exact-result-type", _Int32x4List)
factory Int32x4List(int length) native "TypedData_Int32x4Array_new";
@patch
@ -2708,6 +2738,7 @@ class Int32x4List {
class _Int32x4List extends _TypedList
with _Int32x4ListMixin
implements Int32x4List {
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2744,6 +2775,7 @@ class _Int32x4List extends _TypedList
@patch
class Float64x2List {
@patch
@pragma("vm:exact-result-type", _Float64x2List)
factory Float64x2List(int length) native "TypedData_Float64x2Array_new";
@patch
@ -2757,6 +2789,7 @@ class Float64x2List {
class _Float64x2List extends _TypedList
with _Float64x2ListMixin
implements Float64x2List {
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -2825,6 +2858,7 @@ class _ExternalUint8Array extends _TypedList
with _IntListMixin
implements Uint8List {
// Method(s) implementing the List interface.
@pragma("vm:exact-result-type", "dart:core#_Smi")
int operator [](int index) {
if (index < 0 || index >= length) {
throw new RangeError.index(index, this, "index");
@ -3310,70 +3344,106 @@ class _ExternalFloat64x2Array extends _TypedList
@patch
class Float32x4 {
@patch
@pragma("vm:exact-result-type", _Float32x4)
factory Float32x4(double x, double y, double z, double w)
native "Float32x4_fromDoubles";
@patch
@pragma("vm:exact-result-type", _Float32x4)
factory Float32x4.splat(double v) native "Float32x4_splat";
@patch
@pragma("vm:exact-result-type", _Float32x4)
factory Float32x4.zero() native "Float32x4_zero";
@patch
@pragma("vm:exact-result-type", _Float32x4)
factory Float32x4.fromInt32x4Bits(Int32x4 x)
native "Float32x4_fromInt32x4Bits";
@patch
@pragma("vm:exact-result-type", _Float32x4)
factory Float32x4.fromFloat64x2(Float64x2 v) native "Float32x4_fromFloat64x2";
}
@pragma("vm:entry-point")
class _Float32x4 implements Float32x4 {
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 operator +(Float32x4 other) native "Float32x4_add";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 operator -() native "Float32x4_negate";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 operator -(Float32x4 other) native "Float32x4_sub";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 operator *(Float32x4 other) native "Float32x4_mul";
Float32x4 operator /(Float32x4 other) native "Float32x4_div";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 lessThan(Float32x4 other) native "Float32x4_cmplt";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 lessThanOrEqual(Float32x4 other) native "Float32x4_cmplte";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 greaterThan(Float32x4 other) native "Float32x4_cmpgt";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 greaterThanOrEqual(Float32x4 other) native "Float32x4_cmpgte";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 equal(Float32x4 other) native "Float32x4_cmpequal";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 notEqual(Float32x4 other) native "Float32x4_cmpnequal";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 scale(double s) native "Float32x4_scale";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 abs() native "Float32x4_abs";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 clamp(Float32x4 lowerLimit, Float32x4 upperLimit)
native "Float32x4_clamp";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get x native "Float32x4_getX";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get y native "Float32x4_getY";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get z native "Float32x4_getZ";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get w native "Float32x4_getW";
int get signMask native "Float32x4_getSignMask";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 shuffle(int mask) native "Float32x4_shuffle";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 shuffleMix(Float32x4 zw, int mask) native "Float32x4_shuffleMix";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 withX(double x) native "Float32x4_setX";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 withY(double y) native "Float32x4_setY";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 withZ(double z) native "Float32x4_setZ";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 withW(double w) native "Float32x4_setW";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 min(Float32x4 other) native "Float32x4_min";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 max(Float32x4 other) native "Float32x4_max";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 sqrt() native "Float32x4_sqrt";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 reciprocal() native "Float32x4_reciprocal";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 reciprocalSqrt() native "Float32x4_reciprocalSqrt";
}
@patch
class Int32x4 {
@patch
@pragma("vm:exact-result-type", _Int32x4)
factory Int32x4(int x, int y, int z, int w) native "Int32x4_fromInts";
@patch
@pragma("vm:exact-result-type", _Int32x4)
factory Int32x4.bool(bool x, bool y, bool z, bool w)
native "Int32x4_fromBools";
@patch
@pragma("vm:exact-result-type", _Int32x4)
factory Int32x4.fromFloat32x4Bits(Float32x4 x)
native "Int32x4_fromFloat32x4Bits";
}
@ -3390,20 +3460,31 @@ class _Int32x4 implements Int32x4 {
int get z native "Int32x4_getZ";
int get w native "Int32x4_getW";
int get signMask native "Int32x4_getSignMask";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 shuffle(int mask) native "Int32x4_shuffle";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 shuffleMix(Int32x4 zw, int mask) native "Int32x4_shuffleMix";
Int32x4 withX(int x) native "Int32x4_setX";
Int32x4 withY(int y) native "Int32x4_setY";
Int32x4 withZ(int z) native "Int32x4_setZ";
Int32x4 withW(int w) native "Int32x4_setW";
@pragma("vm:exact-result-type", bool)
bool get flagX native "Int32x4_getFlagX";
@pragma("vm:exact-result-type", bool)
bool get flagY native "Int32x4_getFlagY";
@pragma("vm:exact-result-type", bool)
bool get flagZ native "Int32x4_getFlagZ";
@pragma("vm:exact-result-type", bool)
bool get flagW native "Int32x4_getFlagW";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 withFlagX(bool x) native "Int32x4_setFlagX";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 withFlagY(bool y) native "Int32x4_setFlagY";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 withFlagZ(bool z) native "Int32x4_setFlagZ";
@pragma("vm:exact-result-type", _Int32x4)
Int32x4 withFlagW(bool w) native "Int32x4_setFlagW";
@pragma("vm:exact-result-type", _Float32x4)
Float32x4 select(Float32x4 trueValue, Float32x4 falseValue)
native "Int32x4_select";
}
@ -3411,36 +3492,50 @@ class _Int32x4 implements Int32x4 {
@patch
class Float64x2 {
@patch
@pragma("vm:exact-result-type", _Float64x2)
factory Float64x2(double x, double y) native "Float64x2_fromDoubles";
@patch
@pragma("vm:exact-result-type", _Float64x2)
factory Float64x2.splat(double v) native "Float64x2_splat";
@patch
@pragma("vm:exact-result-type", _Float64x2)
factory Float64x2.zero() native "Float64x2_zero";
@patch
@pragma("vm:exact-result-type", _Float64x2)
factory Float64x2.fromFloat32x4(Float32x4 v) native "Float64x2_fromFloat32x4";
}
@pragma("vm:entry-point")
class _Float64x2 implements Float64x2 {
Float64x2 operator +(Float64x2 other) native "Float64x2_add";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 operator -() native "Float64x2_negate";
Float64x2 operator -(Float64x2 other) native "Float64x2_sub";
Float64x2 operator *(Float64x2 other) native "Float64x2_mul";
Float64x2 operator /(Float64x2 other) native "Float64x2_div";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 scale(double s) native "Float64x2_scale";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 abs() native "Float64x2_abs";
Float64x2 clamp(Float64x2 lowerLimit, Float64x2 upperLimit)
native "Float64x2_clamp";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get x native "Float64x2_getX";
@pragma("vm:exact-result-type", "dart:core#_Double")
double get y native "Float64x2_getY";
int get signMask native "Float64x2_getSignMask";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 withX(double x) native "Float64x2_setX";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 withY(double y) native "Float64x2_setY";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 min(Float64x2 other) native "Float64x2_min";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 max(Float64x2 other) native "Float64x2_max";
@pragma("vm:exact-result-type", _Float64x2)
Float64x2 sqrt() native "Float64x2_sqrt";
}
@ -4432,6 +4527,7 @@ int _toUint8(int value) {
return value & 0xFF;
}
@pragma("vm:exact-result-type", "dart:core#_Smi")
int _toClampedUint8(int value) {
if (value < 0) return 0;
if (value > 0xFF) return 0xFF;

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:observatory/debugger.dart';
import 'package:observatory/service_io.dart';
@ -67,7 +68,7 @@ var tests = [
}
isolate.resume();
},
}
];
main(args) {

View file

@ -2747,10 +2747,13 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
}
}
volatile int ctr = 0;
void ClassFinalizer::FinalizeClass(const Class& cls) {
Thread* thread = Thread::Current();
HANDLESCOPE(thread);
ASSERT(cls.is_type_finalized());
if (strstr(cls.ToCString(), "AssertionError")) ++ctr;
if (cls.is_finalized()) {
return;
}
@ -2800,6 +2803,7 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
ApplyMixinMembers(cls);
}
// Mark as parsed and finalized.
if (strstr(cls.ToCString(), "AssertionError")) ++ctr;
cls.Finalize();
// Mixin app alias classes may still lack their forwarding constructor.
if (cls.is_mixin_app_alias() &&

View file

@ -624,43 +624,6 @@ void PrecompilerEntryPointsPrinter::Print() {
}
writer.CloseArray(); // roots
writer.OpenObject("native-methods");
GrowableObjectArray& recognized_methods = GrowableObjectArray::Handle(
Z, MethodRecognizer::QueryRecognizedMethods(Z));
ASSERT(!recognized_methods.IsNull());
for (intptr_t i = 0; i < recognized_methods.Length(); ++i) {
const Function& func = Function::CheckedHandle(Z, recognized_methods.At(i));
if (!func.is_native()) {
continue;
}
intptr_t result_cid = MethodRecognizer::ResultCid(func);
if (result_cid == kDynamicCid) {
continue;
}
ASSERT(Isolate::Current()->class_table()->HasValidClassAt(result_cid));
writer.OpenArray(String::Handle(func.native_name()).ToCString());
writer.OpenObject();
writer.PrintProperty("action", "return");
const Class& result_cls =
Class::Handle(Isolate::Current()->class_table()->At(result_cid));
DescribeClass(&writer, result_cls);
writer.PrintPropertyBool("nullable", false);
writer.CloseObject();
writer.CloseArray();
}
writer.CloseObject(); // native-methods
writer.CloseObject(); // top-level
const char* contents = writer.ToCString();

View file

@ -1081,7 +1081,7 @@ ConstantHelper::ConstantHelper(Zone* zone,
const Array& ConstantHelper::ReadConstantTable() {
const intptr_t number_of_constants = helper_.ReadUInt();
if (number_of_constants == 0) {
return Array::Handle(Z, Array::null());
return Array::empty_array();
}
const Library& corelib = Library::Handle(Z, Library::CoreLibrary());

View file

@ -35,10 +35,79 @@ intptr_t MethodRecognizer::NumArgsCheckedForStaticCall(
}
intptr_t MethodRecognizer::ResultCid(const Function& function) {
// Use the 'vm:exact-result-type' annotation if available. This can only be
// used within the core library, see 'result_type_pragma.md', detail 1.2 for
// explanation.
Class& cls = Thread::Current()->ClassHandle();
Library& lib = Thread::Current()->LibraryHandle();
cls = function.Owner();
lib = cls.library();
const bool can_use_pragma =
function.kernel_offset() > 0 && lib.IsAnyCoreLibrary();
cls = Class::null();
if (can_use_pragma) {
Isolate* I = Isolate::Current();
auto& option = Object::Handle();
if (function.FindPragma(I, Symbols::vm_exact_result_type(), &option)) {
if (option.IsType()) {
return Type::Cast(option).type_class_id();
} else if (option.IsString()) {
auto& str = String::Cast(option);
// 'str' should match the pattern '([^#]+)#([^#\?]+)' where group 1
// is the library URI and group 2 is the class name.
bool parse_failure = false;
intptr_t library_end = -1;
for (intptr_t i = 0; i < str.Length(); ++i) {
if (str.CharAt(i) == '#') {
if (library_end != -1) {
parse_failure = true;
break;
} else {
library_end = i;
}
}
}
if (!parse_failure && library_end > 0) {
auto& libraryUri = String::Handle(
String::SubString(str, 0, library_end, Heap::kOld));
auto& className = String::Handle(
String::SubString(str, library_end + 1,
str.Length() - library_end - 1, Heap::kOld));
Library& lib = Library::Handle(
Library::LookupLibrary(Thread::Current(), libraryUri));
if (!lib.IsNull()) {
Class& klass =
Class::Handle(lib.LookupClassAllowPrivate(className));
if (!klass.IsNull()) {
return klass.id();
}
}
}
}
}
}
// No result-type annotation can be used, so fall back on the table of
// recognized methods.
switch (function.recognized_kind()) {
#define DEFINE_CASE(cname, fname, ename, result_type, fingerprint) \
case k##ename: \
return k##result_type##Cid;
case k##ename: { \
const intptr_t cid = k##result_type##Cid; \
if (FLAG_strong && cid != kDynamicCid) { \
String& err = String::Handle(); \
err = function.QualifiedScrubbedName(); \
err = String::Concat( \
err, \
String::Handle(String::New(" (MethodRecognizer::k" #ename \
") should be using pragma annotation" \
" rather than method recognizer.", \
Heap::kOld)), \
Heap::kOld); \
FATAL(err.ToCString()); \
} \
return cid; \
}
RECOGNIZED_LIST(DEFINE_CASE)
#undef DEFINE_CASE
default:

View file

@ -0,0 +1,33 @@
# Result type @pragma annotations
To facilitate type-flow analysis and other optimizations, Dart methods may use the pragma `vm:exact-result-type` to declare an exact return type different than the return type in the signature of the method. There are three limitations on this pragma:
0. The Dart object returned by the method at runtime must have exactly the type specified in the annotation (not a subtype).
1. The exact return type declared in the pragma must be a subtype of the return type declared in the method signature.
Note that this restriction is not enforced automatically by the compiler.
2. `vm:exact-result-type` may only be attached to methods in the core library.
This pragma can introduce unsafe behavior since it allows the compiler to make stronger assumptions during optimization than what the sound strong-mode type system allows, so it is only allowed in the core library where the Dart VM team can ensure that it is not misused.
If limitations 0 or 1 are violated, undefined behavior may result.
Note that since `null` is an instance of the `Null` type, which is a subtype of any other, exactness of the annotated result type implies that the result must be non-null.
## Syntax
### Reference to type via type literal
```dart
class A {}
class B extends A {}
@pragma("vm:exact-result-type", B)
A foo() native "foo_impl";
```
### Reference to type via path
```dart
@pragma("vm:exact-result-type", "dart:core#_Smi");
int foo() native "foo_impl";
```

View file

@ -1633,7 +1633,7 @@ TEST_CASE(IsolateReload_TearOff_Parameter_Count_Mismatch) {
"Tried calling: C.foo()\n"
"Found: C.foo(dynamic) => dynamic\n"
"#0 Object.noSuchMethod "
"(dart:core/runtime/libobject_patch.dart:48:5)\n"
"(dart:core/runtime/libobject_patch.dart:50:5)\n"
"#1 main (file:///test-lib:8:12)";
} else {
error =
@ -1643,7 +1643,7 @@ TEST_CASE(IsolateReload_TearOff_Parameter_Count_Mismatch) {
"Tried calling: C.foo()\n"
"Found: C.foo(dynamic) => dynamic\n"
"#0 Object.noSuchMethod "
"(dart:core-patch/dart:core/object_patch.dart:48)\n"
"(dart:core-patch/dart:core/object_patch.dart:50)\n"
"#1 main (test-lib:8:12)";
}
EXPECT_ERROR(error_handle, error);

View file

@ -182,6 +182,7 @@ KernelLoader::KernelLoader(Program* program)
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
pragma_class_(Class::Handle(Z)),
expression_evaluation_library_(Library::Handle(Z)),
@ -345,6 +346,7 @@ KernelLoader::KernelLoader(const Script& script,
external_name_class_(Class::Handle(Z)),
external_name_field_(Field::Handle(Z)),
potential_natives_(GrowableObjectArray::Handle(Z)),
potential_pragma_functions_(GrowableObjectArray::Handle(Z)),
potential_extension_libraries_(GrowableObjectArray::Handle(Z)),
pragma_class_(Class::Handle(Z)),
expression_evaluation_library_(Library::Handle(Z)),
@ -381,6 +383,27 @@ const Array& KernelLoader::ReadConstantTable() {
return helper.ReadConstantTable();
}
void KernelLoader::EvaluateDelayedPragmas() {
if (potential_pragma_functions_.IsNull()) return;
Thread* thread = Thread::Current();
NoOOBMessageScope no_msg_scope(thread);
NoReloadScope no_reload_scope(thread->isolate(), thread);
Function& function = Function::Handle();
Library& library = Library::Handle();
Class& klass = Class::Handle();
for (int i = 0; i < potential_pragma_functions_.Length(); ++i) {
function ^= potential_pragma_functions_.At(i);
klass = function.Owner();
library = klass.library();
library.GetMetadata(function);
}
potential_pragma_functions_ = GrowableObjectArray::null();
kernel_program_info_.set_potential_pragma_functions(
GrowableObjectArray::Handle(Z));
}
void KernelLoader::AnnotateNativeProcedures(const Array& constant_table_array) {
KernelConstantsMap constant_table(constant_table_array.raw());
potential_natives_ = kernel_program_info_.potential_natives();
@ -603,6 +626,8 @@ RawObject* KernelLoader::LoadProgram(bool process_pending_classes) {
kernel_program_info_.set_constants(constants);
kernel_program_info_.set_constants_table(ExternalTypedData::Handle(Z));
EvaluateDelayedPragmas();
NameIndex main = program_->main_method();
if (main == -1) {
return Library::null();
@ -1526,6 +1551,7 @@ void KernelLoader::ReadVMAnnotations(intptr_t annotation_count,
uint8_t tag = helper_.ReadTag();
if (tag == kInstanceConstant) {
*has_pragma_annotation =
*has_pragma_annotation ||
IsClassName(helper_.ReadCanonicalNameReference(),
Symbols::DartCore(), Symbols::Pragma());
}
@ -1680,6 +1706,18 @@ void KernelLoader::LoadProcedure(const Library& library,
library.AddFunctionMetadata(function, TokenPosition::kNoSource,
procedure_offset);
}
if (has_pragma_annotation) {
if (kernel_program_info_.constants() == Array::null()) {
EnsurePotentialPragmaFunctions();
potential_pragma_functions_.Add(function);
} else {
Thread* thread = Thread::Current();
NoOOBMessageScope no_msg_scope(thread);
NoReloadScope no_reload_scope(thread->isolate(), thread);
library.GetMetadata(function);
}
}
}
const Object& KernelLoader::ClassForScriptAt(const Class& klass,

View file

@ -165,6 +165,7 @@ class KernelLoader : public ValueObject {
void AnnotateNativeProcedures(const Array& constant_table);
void LoadNativeExtensionLibraries(const Array& constant_table);
void EvaluateDelayedPragmas();
void ReadVMAnnotations(intptr_t annotation_count,
String* native_name,
@ -299,6 +300,18 @@ class KernelLoader : public ValueObject {
}
}
void EnsurePotentialPragmaFunctions() {
potential_pragma_functions_ =
kernel_program_info_.potential_pragma_functions();
if (potential_pragma_functions_.IsNull()) {
// To avoid too many grows in this array, we'll set it's initial size to
// something close to the actual number of potential native functions.
potential_pragma_functions_ = GrowableObjectArray::New(100, Heap::kNew);
kernel_program_info_.set_potential_pragma_functions(
potential_pragma_functions_);
}
}
void EnsurePotentialExtensionLibraries() {
if (potential_extension_libraries_.IsNull()) {
potential_extension_libraries_ = GrowableObjectArray::New();
@ -331,6 +344,7 @@ class KernelLoader : public ValueObject {
Class& external_name_class_;
Field& external_name_field_;
GrowableObjectArray& potential_natives_;
GrowableObjectArray& potential_pragma_functions_;
GrowableObjectArray& potential_extension_libraries_;
Class& pragma_class_;

View file

@ -11018,6 +11018,13 @@ static void ReportTooManyImports(const Library& lib) {
UNREACHABLE();
}
bool Library::IsAnyCoreLibrary() const {
String& url_str = Thread::Current()->StringHandle();
url_str = url();
return url_str.StartsWith(Symbols::DartScheme()) ||
url_str.StartsWith(Symbols::DartSchemePrivate());
}
void Library::set_num_imports(intptr_t value) const {
if (!Utils::IsUint(16, value)) {
ReportTooManyImports(*this);
@ -11297,7 +11304,6 @@ RawString* Library::MakeMetadataName(const Object& obj) const {
}
RawField* Library::GetMetadataField(const String& metaname) const {
ASSERT(Thread::Current()->IsMutatorThread());
const GrowableObjectArray& metadata =
GrowableObjectArray::Handle(this->metadata());
Field& entry = Field::Handle();
@ -13270,6 +13276,11 @@ void KernelProgramInfo::set_potential_natives(
StorePointer(&raw_ptr()->potential_natives_, candidates.raw());
}
void KernelProgramInfo::set_potential_pragma_functions(
const GrowableObjectArray& candidates) const {
StorePointer(&raw_ptr()->potential_pragma_functions_, candidates.raw());
}
RawError* Library::CompileAll() {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();

View file

@ -697,7 +697,7 @@ class Object {
static RawClass* namespace_class_; // Class of Namespace vm object.
static RawClass* kernel_program_info_class_; // Class of KernelProgramInfo vm
// object.
static RawClass* code_class_; // Class of the Code vm object.
static RawClass* code_class_; // Class of the Code vm object.
static RawClass* instructions_class_; // Class of the Instructions vm object.
static RawClass* object_pool_class_; // Class of the ObjectPool vm object.
static RawClass* pc_descriptors_class_; // Class of PcDescriptors vm object.
@ -4093,6 +4093,9 @@ class Library : public Object {
bool IsCoreLibrary() const { return raw() == CoreLibrary(); }
// Includes 'dart:async', 'dart:typed_data', etc.
bool IsAnyCoreLibrary() const;
inline intptr_t UrlHash() const;
RawExternalTypedData* kernel_data() const { return raw_ptr()->kernel_data_; }
@ -4314,6 +4317,12 @@ class KernelProgramInfo : public Object {
}
void set_potential_natives(const GrowableObjectArray& candidates) const;
RawGrowableObjectArray* potential_pragma_functions() const {
return raw_ptr()->potential_pragma_functions_;
}
void set_potential_pragma_functions(
const GrowableObjectArray& candidates) const;
RawScript* ScriptAt(intptr_t index) const;
private:

View file

@ -1316,6 +1316,7 @@ class RawKernelProgramInfo : public RawObject {
RawArray* scripts_;
RawArray* constants_;
RawGrowableObjectArray* potential_natives_;
RawGrowableObjectArray* potential_pragma_functions_;
RawExternalTypedData* constants_table_;
VISIT_TO(RawObject*, constants_table_);

View file

@ -463,6 +463,7 @@ class ObjectPointerVisitor;
V(DebugProcedureName, ":Eval") \
V(DebugClassName, "#DebugClass") \
V(vm_entry_point, "vm:entry-point") \
V(vm_exact_result_type, "vm:exact-result-type") \
V(Get, "get") \
V(Set, "set") \
V(vm_trace_entrypoints, "vm:testing.unsafe.trace-entrypoints-fn") \