| // Copyright (c) 2012, 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. |
| |
| /** |
| * |
| * Built-in types, collections, |
| * and other core functionality for every Dart program. |
| * |
| * This library is automatically imported. |
| * |
| * Some classes in this library, |
| * such as [String] and [num], |
| * support Dart's built-in data types. |
| * Other classes, such as [List] and [Map], provide data structures |
| * for managing collections of objects. |
| * And still other classes represent commonly used types of data |
| * such as URIs, dates and times, and errors. |
| * |
| * ## Numbers and booleans |
| * |
| * [int] and [double] provide support for Dart's built-in numerical data types: |
| * integers and double-precision floating point numbers, respectively. |
| * An object of type [bool] is either true or false. |
| * Variables of these types can be constructed from literals: |
| * |
| * int meaningOfLife = 42; |
| * double valueOfPi = 3.141592; |
| * bool visible = true; |
| * |
| * ## Strings and regular expressions |
| * |
| * A [String] is immutable and represents a sequence of characters. |
| * |
| * String shakespeareQuote = "All the world's a stage, ..."; |
| * |
| * [StringBuffer] provides a way to construct strings efficiently. |
| * |
| * StringBuffer moreShakespeare = new StringBuffer(); |
| * moreShakespeare.write('And all the men and women '); |
| * moreShakespeare.write('merely players; ...'); |
| * |
| * The String and StringBuffer classes implement string concatenation, |
| * interpolation, and other string manipulation features. |
| * |
| * String philosophy = 'Live on '; |
| * String get palindrome => philosophy + philosophy.split('').reversed.join(); |
| * |
| * [RegExp] implements Dart regular expressions, |
| * which provide a grammar for matching patterns within text. |
| * For example, here's a regular expression that matches |
| * a string of one or more digits: |
| * |
| * var numbers = new RegExp(r'\d+'); |
| * |
| * Dart regular expressions have the same syntax and semantics as |
| * JavaScript regular expressions. See |
| * <http://ecma-international.org/ecma-262/5.1/#sec-15.10> |
| * for the specification of JavaScript regular expressions. |
| * |
| * ## Collections |
| * |
| * The dart:core library provides basic collections, |
| * such as [List], [Map], and [Set]. |
| * |
| * A List is an ordered collection of objects, with a length. |
| * Lists are sometimes called arrays. |
| * Use a List when you need to access objects by index. |
| * |
| * List superheroes = [ 'Batman', 'Superman', 'Harry Potter' ]; |
| * |
| * A Set is an unordered collection of unique objects. |
| * You cannot get an item by index (position). |
| * Adding a duplicate item has no effect. |
| * |
| * Set villains = new Set(); |
| * villains.add('Joker'); |
| * villains.addAll( ['Lex Luther', 'Voldemort'] ); |
| * |
| * A Map is an unordered collection of key-value pairs. |
| * Maps are sometimes called associative arrays because |
| * maps associate a key to some value for easy retrieval. |
| * Keys are unique. |
| * Use a Map when you need to access objects |
| * by a unique identifier. |
| * |
| * Map sidekicks = { 'Batman': 'Robin', |
| * 'Superman': 'Lois Lane', |
| * 'Harry Potter': 'Ron and Hermione' }; |
| * |
| * In addition to these classes, |
| * dart:core contains [Iterable], |
| * an interface that defines functionality |
| * common in collections of objects. |
| * Examples include the ability |
| * to run a function on each element in the collection, |
| * to apply a test to each element, |
| * to retrieve an object, and to determine length. |
| * |
| * Iterable is implemented by List and Set, |
| * and used by Map for its keys and values. |
| * |
| * For other kinds of collections, check out the |
| * [dart:collection](#dart-collection) library. |
| * |
| * ## Date and time |
| * |
| * Use [DateTime] to represent a point in time |
| * and [Duration] to represent a span of time. |
| * |
| * You can create DateTime objects with constructors |
| * or by parsing a correctly formatted string. |
| * |
| * DateTime now = new DateTime.now(); |
| * DateTime berlinWallFell = new DateTime(1989, 11, 9); |
| * DateTime moonLanding = DateTime.parse("1969-07-20"); |
| * |
| * Create a Duration object specifying the individual time units. |
| * |
| * Duration timeRemaining = new Duration(hours:56, minutes:14); |
| * |
| * In addition to DateTime and Duration, |
| * dart:core contains the [Stopwatch] class for measuring elapsed time. |
| * |
| * ## Uri |
| * |
| * A [Uri] object represents a uniform resource identifier, |
| * which identifies a resource on the web. |
| * |
| * Uri dartlang = Uri.parse('http://dartlang.org/'); |
| * |
| * ## Errors |
| * |
| * The [Error] class represents the occurrence of an error |
| * during runtime. |
| * Subclasses of this class represent specific kinds of errors. |
| * |
| * ## Other documentation |
| * |
| * For more information about how to use the built-in types, refer to [Built-in |
| * Types](http://www.dartlang.org/docs/dart-up-and-running/contents/ch02.html#built-in-types) |
| * in Chapter 2 of |
| * [Dart: Up and Running](http://www.dartlang.org/docs/dart-up-and-running/). |
| * |
| * Also, see [dart:core - Numbers, Collections, Strings, and |
| * More](https://www.dartlang.org/docs/dart-up-and-running/ch03.html#dartcore---numbers-collections-strings-and-more) |
| * for more coverage of classes in this package. |
| * |
| * The |
| * [Dart Language Specification](http://www.dartlang.org/docs/spec/) |
| * provides technical details. |
| */ |
| library dart.core; |
| |
| import "dart:collection"; |
| import "dart:_internal" hide Symbol; |
| import "dart:_internal" as internal show Symbol; |
| import "dart:convert" |
| show Encoding, ASCII, LATIN1, UTF8, BASE64, StringConversionSink; |
| import "dart:math" show Random; // Used by List.shuffle. |
| import "dart:typed_data" show Uint8List; |
| import "dart:async"; |
| import "dart:collection" show LinkedList, LinkedListEntry; |
| import 'dart:convert' show ASCII, JSON; |
| import "dart:isolate"; |
| import "dart:math"; |
| import "dart:typed_data"; |
| import 'dart:_internal' as internal; |
| |
| part "annotations.dart"; |
| part "bool.dart"; |
| part "comparable.dart"; |
| part "date_time.dart"; |
| part "double.dart"; |
| part "duration.dart"; |
| part "errors.dart"; |
| part "exceptions.dart"; |
| part "expando.dart"; |
| part "function.dart"; |
| part "identical.dart"; |
| part "int.dart"; |
| part "invocation.dart"; |
| part "iterable.dart"; |
| part "iterator.dart"; |
| part "list.dart"; |
| part "map.dart"; |
| part "null.dart"; |
| part "num.dart"; |
| part "object.dart"; |
| part "pattern.dart"; |
| part "print.dart"; |
| part "regexp.dart"; |
| part "set.dart"; |
| part "sink.dart"; |
| part "stacktrace.dart"; |
| part "stopwatch.dart"; |
| part "string.dart"; |
| part "string_buffer.dart"; |
| part "string_sink.dart"; |
| part "symbol.dart"; |
| part "type.dart"; |
| part "uri.dart"; |
| |
| class _CastError extends Error implements CastError { |
| _CastError._create(this._url, this._line, this._column, this._errorMsg); |
| |
| // A CastError is allocated by TypeError._throwNew() when dst_name equals |
| // Symbols::InTypeCast(). |
| |
| String toString() => _errorMsg; |
| |
| // Fields _url, _line, and _column are only used for debugging purposes. |
| final String _url; |
| final int _line; |
| final int _column; |
| final String _errorMsg; |
| } |
| typedef bool _SyncGeneratorCallback(Iterator iterator); |
| class _SyncIterable extends IterableBase { |
| // _moveNextFn is the closurized body of the generator function. |
| final _SyncGeneratorCallback _moveNextFn; |
| |
| const _SyncIterable(this._moveNextFn); |
| |
| get iterator { |
| return new _SyncIterator(_moveNextFn._clone()); |
| } |
| } |
| class _SyncIterator implements Iterator { |
| bool isYieldEach; // Set by generated code for the yield* statement. |
| Iterator yieldEachIterator; |
| var _current; // Set by generated code for the yield and yield* statement. |
| _SyncGeneratorCallback _moveNextFn; |
| |
| get current => |
| yieldEachIterator != null ? yieldEachIterator.current : _current; |
| |
| _SyncIterator(this._moveNextFn); |
| |
| bool moveNext() { |
| if (_moveNextFn == null) { |
| return false; |
| } |
| while (true) { |
| if (yieldEachIterator != null) { |
| if (yieldEachIterator.moveNext()) { |
| return true; |
| } |
| yieldEachIterator = null; |
| } |
| isYieldEach = false; |
| // _moveNextFn() will update the values of isYieldEach and _current. |
| if (!_moveNextFn(this)) { |
| _moveNextFn = null; |
| _current = null; |
| return false; |
| } |
| if (isYieldEach) { |
| // Spec mandates: it is a dynamic error if the class of [the object |
| // returned by yield*] does not implement Iterable. |
| yieldEachIterator = (_current as Iterable).iterator; |
| _current = null; |
| continue; |
| } |
| return true; |
| } |
| } |
| } |
| class _List<E> extends FixedLengthListBase<E> { |
| factory _List(int length) native "List_allocate"; |
| |
| E operator [](int index) native "List_getIndexed"; |
| |
| void operator []=(int index, E value) native "List_setIndexed"; |
| |
| int get length native "List_getLength"; |
| |
| List _slice(int start, int count, bool needsTypeArgument) { |
| if (count <= 64) { |
| final result = needsTypeArgument ? new _List<E>(count) : new _List(count); |
| for (int i = 0; i < result.length; i++) { |
| result[i] = this[start + i]; |
| } |
| return result; |
| } else { |
| return _sliceInternal(start, count, needsTypeArgument); |
| } |
| } |
| |
| List _sliceInternal(int start, int count, bool needsTypeArgument) |
| native "List_slice"; |
| |
| // List interface. |
| void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { |
| if (start < 0 || start > this.length) { |
| throw new RangeError.range(start, 0, this.length); |
| } |
| if (end < start || end > this.length) { |
| throw new RangeError.range(end, start, this.length); |
| } |
| int length = end - start; |
| if (length == 0) return; |
| if (identical(this, iterable)) { |
| Lists.copy(iterable, skipCount, this, start, length); |
| } else if (ClassID.getID(iterable) == ClassID.cidArray) { |
| Lists.copy(iterable, skipCount, this, start, length); |
| } else if (iterable is List) { |
| Lists.copy(iterable, skipCount, this, start, length); |
| } else { |
| Iterator it = iterable.iterator; |
| while (skipCount > 0) { |
| if (!it.moveNext()) return; |
| skipCount--; |
| } |
| for (int i = start; i < end; i++) { |
| if (!it.moveNext()) return; |
| this[i] = it.current; |
| } |
| } |
| } |
| |
| List<E> sublist(int start, [int end]) { |
| end = RangeError.checkValidRange(start, end, this.length); |
| int length = end - start; |
| if (length == 0) return <E>[]; |
| var result = new _GrowableList<E>.withData(_slice(start, length, false)); |
| result._setLength(length); |
| return result; |
| } |
| |
| // Iterable interface. |
| |
| void forEach(f(E element)) { |
| final length = this.length; |
| for (int i = 0; i < length; i++) { |
| f(this[i]); |
| } |
| } |
| |
| Iterator<E> get iterator { |
| return new _FixedSizeArrayIterator<E>(this); |
| } |
| |
| E get first { |
| if (length > 0) return this[0]; |
| throw IterableElementError.noElement(); |
| } |
| |
| E get last { |
| if (length > 0) return this[length - 1]; |
| throw IterableElementError.noElement(); |
| } |
| |
| E get single { |
| if (length == 1) return this[0]; |
| if (length == 0) throw IterableElementError.noElement(); |
| throw IterableElementError.tooMany(); |
| } |
| |
| List<E> toList({bool growable: true}) { |
| var length = this.length; |
| if (length > 0) { |
| var result = _slice(0, length, !growable); |
| if (growable) { |
| result = new _GrowableList<E>.withData(result); |
| result._setLength(length); |
| } |
| return result; |
| } |
| // _GrowableList.withData must not be called with empty list. |
| return growable ? <E>[] : new List<E>(0); |
| } |
| } |
| class _ImmutableList<E> extends UnmodifiableListBase<E> { |
| factory _ImmutableList._uninstantiable() { |
| throw new UnsupportedError( |
| "ImmutableArray can only be allocated by the VM"); |
| } |
| |
| factory _ImmutableList._from(List from, int offset, int length) |
| native "ImmutableList_from"; |
| |
| E operator [](int index) native "List_getIndexed"; |
| |
| int get length native "List_getLength"; |
| |
| List<E> sublist(int start, [int end]) { |
| end = RangeError.checkValidRange(start, end, this.length); |
| int length = end - start; |
| if (length == 0) return <E>[]; |
| List list = new _List(length); |
| for (int i = 0; i < length; i++) { |
| list[i] = this[start + i]; |
| } |
| var result = new _GrowableList<E>.withData(list); |
| result._setLength(length); |
| return result; |
| } |
| |
| // Collection interface. |
| |
| void forEach(f(E element)) { |
| final length = this.length; |
| for (int i = 0; i < length; i++) { |
| f(this[i]); |
| } |
| } |
| |
| Iterator<E> get iterator { |
| return new _FixedSizeArrayIterator<E>(this); |
| } |
| |
| E get first { |
| if (length > 0) return this[0]; |
| throw IterableElementError.noElement(); |
| } |
| |
| E get last { |
| if (length > 0) return this[length - 1]; |
| throw IterableElementError.noElement(); |
| } |
| |
| E get single { |
| if (length == 1) return this[0]; |
| if (length == 0) throw IterableElementError.noElement(); |
| throw IterableElementError.tooMany(); |
| } |
| |
| List<E> toList({bool growable: true}) { |
| var length = this.length; |
| if (length > 0) { |
| List<E> list = new _List<E>(length); |
| for (int i = 0; i < length; i++) { |
| list[i] = this[i]; |
| } |
| if (!growable) return list; |
| list = new _GrowableList<E>.withData(list); |
| list._setLength(length); |
| return list; |
| } |
| return growable ? <E>[] : new _List<E>(0); |
| } |
| } |
| class _FixedSizeArrayIterator<E> implements Iterator<E> { |
| final List<E> _array; |
| final int _length; // Cache array length for faster access. |
| int _index; |
| E _current; |
| |
| _FixedSizeArrayIterator(List array) |
| : _array = array, |
| _length = array.length, |
| _index = 0 { |
| assert(array is _List || array is _ImmutableList); |
| } |
| |
| E get current => _current; |
| |
| bool moveNext() { |
| if (_index >= _length) { |
| _current = null; |
| return false; |
| } |
| _current = _array[_index]; |
| _index++; |
| return true; |
| } |
| } |
| const int _GROWABLE_ARRAY_MARKER = -42; |
| class _Double implements double { |
| factory _Double.fromInteger(int value) native "Double_doubleFromInteger"; |
| |
| // TODO: Make a stared static method for hashCode and _identityHashCode |
| // when semantics are corrected as described in: |
| // https://github.com/dart-lang/sdk/issues/2884 |
| int get hashCode => (isNaN || isInfinite) ? 0 : toInt(); |
| int get _identityHashCode => (isNaN || isInfinite) ? 0 : toInt(); |
| |
| double operator +(double other) { |
| return _add(other.toDouble()); |
| } |
| |
| double _add(double other) native "Double_add"; |
| |
| double operator -(double other) { |
| return _sub(other.toDouble()); |
| } |
| |
| double _sub(double other) native "Double_sub"; |
| |
| double operator *(double other) { |
| return _mul(other.toDouble()); |
| } |
| |
| double _mul(double other) native "Double_mul"; |
| |
| int operator ~/(double other) { |
| return _trunc_div(other.toDouble()); |
| } |
| |
| int _trunc_div(double other) native "Double_trunc_div"; |
| |
| double operator /(double other) { |
| return _div(other.toDouble()); |
| } |
| |
| double _div(double other) native "Double_div"; |
| |
| double operator %(double other) { |
| return _modulo(other.toDouble()); |
| } |
| |
| double _modulo(double other) native "Double_modulo"; |
| |
| double remainder(double other) { |
| return _remainder(other.toDouble()); |
| } |
| |
| double _remainder(double other) native "Double_remainder"; |
| |
| double operator -() native "Double_flipSignBit"; |
| |
| bool operator ==(double other) { |
| return _equal(other.toDouble()); |
| } |
| |
| bool _equal(double other) native "Double_equal"; |
| bool _equalToInteger(int other) native "Double_equalToInteger"; |
| bool operator <(double other) { |
| return other > this; |
| } |
| |
| bool operator >(double other) { |
| return _greaterThan(other.toDouble()); |
| } |
| |
| bool _greaterThan(double other) native "Double_greaterThan"; |
| bool operator >=(double other) { |
| return (this == other) || (this > other); |
| } |
| |
| bool operator <=(double other) { |
| return (this == other) || (this < other); |
| } |
| |
| double _addFromInteger(int other) { |
| return new _Double.fromInteger(other)._add(this); |
| } |
| |
| double _subFromInteger(int other) { |
| return new _Double.fromInteger(other)._sub(this); |
| } |
| |
| double _mulFromInteger(int other) { |
| return new _Double.fromInteger(other)._mul(this); |
| } |
| |
| int _truncDivFromInteger(int other) { |
| return new _Double.fromInteger(other)._trunc_div(this); |
| } |
| |
| double _moduloFromInteger(int other) { |
| return new _Double.fromInteger(other)._modulo(this); |
| } |
| |
| double _remainderFromInteger(int other) { |
| return new _Double.fromInteger(other)._remainder(this); |
| } |
| |
| bool _greaterThanFromInteger(int other) |
| native "Double_greaterThanFromInteger"; |
| |
| bool get isNegative native "Double_getIsNegative"; |
| bool get isInfinite native "Double_getIsInfinite"; |
| bool get isNaN native "Double_getIsNaN"; |
| bool get isFinite => !isInfinite && !isNaN; // Can be optimized. |
| |
| double abs() { |
| // Handle negative 0.0. |
| if (this == 0.0) return 0.0; |
| return this < 0.0 ? -this : this; |
| } |
| |
| double get sign { |
| if (this > 0.0) return 1.0; |
| if (this < 0.0) return -1.0; |
| return this; // +/-0.0 or NaN. |
| } |
| |
| int round() => roundToDouble().toInt(); |
| int floor() => floorToDouble().toInt(); |
| int ceil() => ceilToDouble().toInt(); |
| int truncate() => truncateToDouble().toInt(); |
| |
| double roundToDouble() native "Double_round"; |
| double floorToDouble() native "Double_floor"; |
| double ceilToDouble() native "Double_ceil"; |
| double truncateToDouble() native "Double_truncate"; |
| |
| double clamp(double lowerLimit, double upperLimit) { |
| if (lowerLimit.compareTo(upperLimit) > 0) { |
| throw new ArgumentError(lowerLimit as Object); |
| } |
| if (lowerLimit.isNaN) return lowerLimit; |
| if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| if (this.compareTo(upperLimit) > 0) return upperLimit; |
| return this; |
| } |
| |
| int toInt() native "Double_toInt"; |
| num _toBigintOrDouble() { |
| return this; |
| } |
| |
| double toDouble() { |
| return this; |
| } |
| |
| static const int CACHE_SIZE_LOG2 = 3; |
| static const int CACHE_LENGTH = 1 << (CACHE_SIZE_LOG2 + 1); |
| static const int CACHE_MASK = CACHE_LENGTH - 1; |
| // Each key (double) followed by its toString result. |
| static final List _cache = new List(CACHE_LENGTH); |
| static int _cacheEvictIndex = 0; |
| |
| String _toString() native "Double_toString"; |
| |
| String toString() { |
| if (0.0 == this) { |
| return "0.0"; |
| } |
| return _toString(); |
| } |
| |
| String toStringAsFixed(int fractionDigits) { |
| // See ECMAScript-262, 15.7.4.5 for details. |
| |
| // Step 2. |
| if (fractionDigits < 0 || fractionDigits > 20) { |
| throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits"); |
| } |
| |
| // Step 3. |
| double x = this; |
| |
| // Step 4. |
| if (isNaN) return "NaN"; |
| |
| // Step 5 and 6 skipped. Will be dealt with by native function. |
| |
| // Step 7. |
| if (x >= 1e21 || x <= -1e21) { |
| return x.toString(); |
| } |
| |
| return _toStringAsFixed(fractionDigits); |
| } |
| |
| String _toStringAsFixed(int fractionDigits) native "Double_toStringAsFixed"; |
| |
| String toStringAsExponential([int fractionDigits = -1]) { |
| // See ECMAScript-262, 15.7.4.6 for details. |
| |
| // The EcmaScript specification checks for NaN and Infinity before looking |
| // at the fractionDigits. In Dart we are consistent with toStringAsFixed and |
| // look at the fractionDigits first. |
| |
| // Step 7. |
| if (fractionDigits < -1 || fractionDigits > 20) { |
| throw new RangeError.range(fractionDigits, 0, 20, "fractionDigits"); |
| } |
| |
| if (isNaN) return "NaN"; |
| if (this == double.INFINITY) return "Infinity"; |
| if (this == -double.INFINITY) return "-Infinity"; |
| |
| return _toStringAsExponential(fractionDigits); |
| } |
| |
| String _toStringAsExponential(int fractionDigits) |
| native "Double_toStringAsExponential"; |
| |
| String toStringAsPrecision(int precision) { |
| // See ECMAScript-262, 15.7.4.7 for details. |
| |
| // The EcmaScript specification checks for NaN and Infinity before looking |
| // at the fractionDigits. In Dart we are consistent with toStringAsFixed and |
| // look at the fractionDigits first. |
| |
| // Step 8. |
| if (precision < 1 || precision > 21) { |
| throw new RangeError.range(precision, 1, 21, "precision"); |
| } |
| |
| if (isNaN) return "NaN"; |
| if (this == double.INFINITY) return "Infinity"; |
| if (this == -double.INFINITY) return "-Infinity"; |
| |
| return _toStringAsPrecision(precision); |
| } |
| |
| String _toStringAsPrecision(int fractionDigits) |
| native "Double_toStringAsPrecision"; |
| |
| // Order is: NaN > Infinity > ... > 0.0 > -0.0 > ... > -Infinity. |
| int compareTo(double other) { |
| const int EQUAL = 0, LESS = -1, GREATER = 1; |
| if (this < other) { |
| return LESS; |
| } else if (this > other) { |
| return GREATER; |
| } else if (this == other) { |
| if (this == 0.0) { |
| bool thisIsNegative = isNegative; |
| bool otherIsNegative = other.isNegative; |
| if (thisIsNegative == otherIsNegative) { |
| return EQUAL; |
| } |
| return thisIsNegative ? LESS : GREATER; |
| } else { |
| return EQUAL; |
| } |
| } else if (isNaN) { |
| return other.isNaN ? EQUAL : GREATER; |
| } else { |
| // Other is NaN. |
| return LESS; |
| } |
| } |
| } |
| class _AssertionError extends Error implements AssertionError { |
| _AssertionError._create( |
| this._failedAssertion, this._url, this._line, this._column, this.message); |
| |
| // AssertionError_throwNew in errors.cc fishes the assertion source code |
| // out of the script. It expects a Dart stack frame from class |
| // _AssertionError. Thus we need a Dart stub that calls the native code. |
| static _throwNew(int assertionStart, int assertionEnd, Object message) { |
| _doThrowNew(assertionStart, assertionEnd, message); |
| } |
| |
| static _doThrowNew(int assertionStart, int assertionEnd, Object message) |
| native "AssertionError_throwNew"; |
| |
| static _evaluateAssertion(condition) { |
| if (identical(condition, true) || identical(condition, false)) { |
| return condition; |
| } |
| if (condition is _Closure) { |
| return condition(); |
| } |
| if (condition is Function) { |
| condition = condition(); |
| } |
| return condition; |
| } |
| |
| String get _messageString { |
| if (message == null) return "is not true."; |
| if (message is String) return message; |
| return Error.safeToString(message); |
| } |
| |
| String toString() { |
| if (_url == null) { |
| if (message == null) return _failedAssertion; |
| return "'$_failedAssertion': $_messageString"; |
| } |
| var columnInfo = ""; |
| if (_column > 0) { |
| // Only add column information if it is valid. |
| columnInfo = " pos $_column"; |
| } |
| return "'$_url': Failed assertion: line $_line$columnInfo: " |
| "'$_failedAssertion': $_messageString"; |
| } |
| |
| final String _failedAssertion; |
| final String _url; |
| final int _line; |
| final int _column; |
| final Object message; |
| } |
| class _TypeError extends _AssertionError implements TypeError { |
| _TypeError._create(String url, int line, int column, String errorMsg) |
| : super._create("is assignable", url, line, column, errorMsg); |
| |
| static _throwNew(int location, Object src_value, _Type dst_type, |
| String dst_name, String bound_error_msg) native "TypeError_throwNew"; |
| /* |
| static _throwNewIfNotLoaded( |
| _LibraryPrefix prefix, |
| int location, |
| Object src_value, |
| _Type dst_type, |
| String dst_name, |
| String bound_error_msg) { |
| if (!prefix.isLoaded()) { |
| _throwNew(location, src_value, dst_type, dst_name, bound_error_msg); |
| } |
| } |
| */ |
| String toString() => super.message; |
| } |
| class _EnumHelper { |
| String _name; |
| String toString() => _name; |
| int get hashCode => _name.hashCode; |
| } |
| class _InternalError { |
| const _InternalError(this._msg); |
| String toString() => "InternalError: '${_msg}'"; |
| final String _msg; |
| } |
| class _CompileTimeError extends Error { |
| final String _errorMsg; |
| _CompileTimeError(this._errorMsg); |
| String toString() => _errorMsg; |
| } |
| class _Closure implements Function { |
| bool operator ==(other) native "Closure_equals"; |
| |
| int get hashCode native "Closure_hashCode"; |
| |
| _Closure get call => this; |
| |
| _Closure _clone() native "Closure_clone"; |
| |
| _invoke() native "Closure_invoke"; // Used as a marker. |
| |
| // No instance fields should be declared before the following 3 fields whose |
| // offsets must be identical in Dart and C++. |
| |
| // The following 3 fields are declared both in raw_object.h (for direct access |
| // from C++ code) and also here so that the offset-to-field map used by |
| // deferred objects is properly initialized. |
| // Caution: These fields are not Dart instances, but VM objects. |
| // The fields had to be renamed here so that they would be private fields |
| // in dart |
| // instantiator_ ===> _instantiator |
| // function_ ===> _function |
| // context_ ===> _context |
| var _instantiator; |
| var _function; |
| var _context; |
| } |
| class _GrowableList<T> extends ListBase<T> { |
| void insert(int index, T element) { |
| if ((index < 0) || (index > length)) { |
| throw new RangeError.range(index, 0, length); |
| } |
| if (index == this.length) { |
| add(element); |
| return; |
| } |
| int oldLength = this.length; |
| // We are modifying the length just below the is-check. Without the check |
| // Array.copy could throw an exception, leaving the list in a bad state |
| // (with a length that has been increased, but without a new element). |
| if (index is! int) throw new ArgumentError(index); |
| this.length++; |
| Lists.copy(this, index, this, index + 1, oldLength - index); |
| _data[index] = element; |
| } |
| |
| T removeAt(int index) { |
| var result = _data[index]; |
| int newLength = this.length - 1; |
| if (index < newLength) { |
| Lists.copy(this, index + 1, this, index, newLength - index); |
| } |
| this.length = newLength; |
| return result; |
| } |
| |
| bool remove(Object element) { |
| for (int i = 0; i < this.length; i++) { |
| if (_data[i] == element) { |
| removeAt(i); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void insertAll(int index, Iterable<T> iterable) { |
| if (index < 0 || index > length) { |
| throw new RangeError.range(index, 0, length); |
| } |
| // TODO(floitsch): we can probably detect more cases. |
| if (iterable is! List && iterable is! Set && iterable is! SubListIterable) { |
| iterable = iterable.toList(); |
| } |
| int insertionLength = iterable.length; |
| // There might be errors after the length change, in which case the list |
| // will end up being modified but the operation not complete. Unless we |
| // always go through a "toList" we can't really avoid that. |
| this.length += insertionLength; |
| setRange(index + insertionLength, this.length, this, index); |
| setAll(index, iterable); |
| } |
| |
| void setAll(int index, Iterable<T> iterable) { |
| if (iterable is List) { |
| setRange(index, index + iterable.length, iterable); |
| } else { |
| for (T element in iterable) { |
| _data[index++] = element; |
| } |
| } |
| } |
| |
| void removeRange(int start, int end) { |
| RangeError.checkValidRange(start, end, this.length); |
| Lists.copy(this, end, this, start, this.length - end); |
| this.length = this.length - (end - start); |
| } |
| |
| List<T> sublist(int start, [int end]) { |
| end = RangeError.checkValidRange(start, end, this.length); |
| int length = end - start; |
| if (length == 0) return <T>[]; |
| List list = new _List(length); |
| for (int i = 0; i < length; i++) { |
| list[i] = _data[start + i]; |
| } |
| var result = new _GrowableList<T>.withData(list); |
| result._setLength(length); |
| return result; |
| } |
| |
| static const int _kDefaultCapacity = 2; |
| |
| factory _GrowableList(int length) { |
| var data = new _List((length == 0) ? _kDefaultCapacity : length); |
| var result = new _GrowableList<T>.withData(data); |
| if (length > 0) { |
| result._setLength(length); |
| } |
| return result; |
| } |
| |
| factory _GrowableList.withCapacity(int capacity) { |
| var data = new _List((capacity == 0) ? _kDefaultCapacity : capacity); |
| return new _GrowableList<T>.withData(data); |
| } |
| |
| factory _GrowableList.withData(_List data) native "GrowableList_allocate"; |
| |
| int get _capacity native "GrowableList_getCapacity"; |
| |
| int get length native "GrowableList_getLength"; |
| |
| void set length(int new_length) { |
| int old_capacity = _capacity; |
| int new_capacity = new_length; |
| if (new_length == 0) { |
| // Ensure that we use _kDefaultCapacity only when the old_capacity |
| // is greater than _kDefaultCapacity otherwise we end up growing the |
| // the array. |
| if (old_capacity < _kDefaultCapacity) { |
| new_capacity = old_capacity; |
| } else { |
| new_capacity = _kDefaultCapacity; |
| } |
| } |
| if (new_capacity > old_capacity) { |
| _grow(new_capacity); |
| _setLength(new_length); |
| return; |
| } |
| // We are shrinking. Pick the method which has fewer writes. |
| // In the shrink-to-fit path, we write |new_capacity + new_length| words |
| // (null init + copy). |
| // In the non-shrink-to-fit path, we write |length - new_length| words |
| // (null overwrite). |
| final bool shouldShrinkToFit = |
| (new_capacity + new_length) < (length - new_length); |
| if (shouldShrinkToFit) { |
| _shrink(new_capacity, new_length); |
| } else { |
| for (int i = new_length; i < length; i++) { |
| _data[i] = null; |
| } |
| } |
| _setLength(new_length); |
| } |
| |
| void _setLength(int new_length) native "GrowableList_setLength"; |
| |
| void _setData(_List array) native "GrowableList_setData"; |
| |
| _List get _data native "GrowableList_getData"; |
| |
| T operator [](int index) native "GrowableList_getIndexed"; |
| |
| void operator []=(int index, T value) native "GrowableList_setIndexed"; |
| |
| // The length of this growable array. It is always less than or equal to the |
| // length of the object array, which itself is always greater than 0, so that |
| // grow() does not have to check for a zero length object array before |
| // doubling its size. |
| void add(T value) { |
| var len = length; |
| if (len == _capacity) { |
| _grow(len * 2); |
| } |
| _setLength(len + 1); |
| _data[len] = value; |
| } |
| |
| void addAll(Iterable<T> iterable) { |
| var len = length; |
| final cid = ClassID.getID(iterable); |
| final isVMList = (cid == ClassID.cidArray) || |
| (cid == ClassID.cidGrowableObjectArray) || |
| (cid == ClassID.cidImmutableArray); |
| if (isVMList || (iterable is EfficientLengthIterable)) { |
| var cap = _capacity; |
| // Pregrow if we know iterable.length. |
| var iterLen = iterable.length; |
| var newLen = len + iterLen; |
| if (newLen > cap) { |
| do { |
| cap *= 2; |
| } while (newLen > cap); |
| _grow(cap); |
| } |
| if (isVMList) { |
| if (identical(iterable, this)) { |
| throw new ConcurrentModificationError(this); |
| } |
| this._setLength(newLen); |
| for (int i = 0; i < iterLen; i++) { |
| _data[len++] = iterable[i]; |
| } |
| return; |
| } |
| } |
| Iterator it = iterable.iterator; |
| if (!it.moveNext()) return; |
| do { |
| while (len < _capacity) { |
| int newLen = len + 1; |
| this._setLength(newLen); |
| _data[len] = it.current; |
| if (!it.moveNext()) return; |
| if (this.length != newLen) throw new ConcurrentModificationError(this); |
| len = newLen; |
| } |
| _grow(_capacity * 2); |
| } while (true); |
| } |
| |
| T removeLast() { |
| var len = length - 1; |
| var elem = _data[len]; |
| this.length = len; |
| return elem; |
| } |
| |
| T get first { |
| if (length > 0) return _data[0]; |
| throw IterableElementError.noElement(); |
| } |
| |
| T get last { |
| if (length > 0) return _data[length - 1]; |
| throw IterableElementError.noElement(); |
| } |
| |
| T get single { |
| if (length == 1) return _data[0]; |
| if (length == 0) throw IterableElementError.noElement(); |
| throw IterableElementError.tooMany(); |
| ; |
| } |
| |
| void _grow(int new_capacity) { |
| final _List new_data = new _List(new_capacity); |
| for (int i = 0; i < length; i++) { |
| new_data[i] = _data[i]; |
| } |
| _setData(new_data); |
| } |
| |
| void _shrink(int new_capacity, int new_length) { |
| final _List new_data = new _List(new_capacity); |
| for (int i = 0; i < new_length; i++) { |
| new_data[i] = _data[i]; |
| } |
| _setData(new_data); |
| } |
| |
| // Iterable interface. |
| |
| void forEach(f(T element)) { |
| int initialLength = length; |
| for (int i = 0; i < length; i++) { |
| f(_data[i]); |
| if (length != initialLength) throw new ConcurrentModificationError(this); |
| } |
| } |
| |
| String join([String separator = ""]) { |
| final int length = this.length; |
| if (length == 0) return ""; |
| if (length == 1) return "${_data[0]}"; |
| if (separator.isNotEmpty) return _joinWithSeparator(separator); |
| var i = 0; |
| var codeUnitCount = 0; |
| while (i < length) { |
| final element = _data[i]; |
| final int cid = ClassID.getID(element); |
| // While list contains one-byte strings. |
| if (ClassID.cidOneByteString == cid) { |
| codeUnitCount += element.length; |
| i++; |
| // Loop back while strings are one-byte strings. |
| continue; |
| } |
| // Otherwise, never loop back to the outer loop, and |
| // handle the remaining strings below. |
| |
| // Loop while elements are strings, |
| final int firstNonOneByteStringLimit = i; |
| var nextElement = element; |
| while (nextElement is String) { |
| i++; |
| if (i == length) { |
| return _StringBase._concatRangeNative(this, 0, length); |
| } |
| nextElement = _data[i]; |
| } |
| |
| // Not all elements are strings, so allocate a new backing array. |
| final list = new _List(length); |
| for (int copyIndex = 0; copyIndex < i; copyIndex++) { |
| list[copyIndex] = _data[copyIndex]; |
| } |
| // Is non-zero if list contains a non-onebyte string. |
| var onebyteCanary = i - firstNonOneByteStringLimit; |
| while (true) { |
| final String elementString = "$nextElement"; |
| onebyteCanary |= |
| (ClassID.getID(elementString) ^ ClassID.cidOneByteString); |
| list[i] = elementString; |
| codeUnitCount += elementString.length; |
| i++; |
| if (i == length) break; |
| nextElement = _data[i]; |
| } |
| if (onebyteCanary == 0) { |
| // All elements returned a one-byte string from toString. |
| return _OneByteString._concatAll(list, codeUnitCount); |
| } |
| return _StringBase._concatRangeNative(list, 0, length); |
| } |
| // All elements were one-byte strings. |
| return _OneByteString._concatAll(this, codeUnitCount); |
| } |
| |
| String _joinWithSeparator(String separator) { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.write(_data[0]); |
| for (int i = 1; i < this.length; i++) { |
| buffer.write(separator); |
| buffer.write(_data[i]); |
| } |
| return buffer.toString(); |
| } |
| |
| T elementAt(int index) { |
| return _data[index]; |
| } |
| |
| bool get isEmpty { |
| return this.length == 0; |
| } |
| |
| bool get isNotEmpty => !isEmpty; |
| |
| void clear() { |
| this.length = 0; |
| } |
| |
| String toString() => ListBase.listToString(this); |
| |
| Iterator<T> get iterator { |
| return new ListIterator<T>(this); |
| } |
| |
| List<T> toList({bool growable: true}) { |
| var length = this.length; |
| if (length > 0) { |
| List list = growable ? new _List(length) : new _List<T>(length); |
| for (int i = 0; i < length; i++) { |
| list[i] = _data[i]; |
| } |
| if (!growable) return list; |
| var result = new _GrowableList<T>.withData(list); |
| result._setLength(length); |
| return result; |
| } |
| return growable ? <T>[] : new List<T>(0); |
| } |
| |
| Set<T> toSet() { |
| return new Set<T>.from(this); |
| } |
| } |
| class _ImmutableMap<K, V> implements Map<K, V> { |
| final _ImmutableList _kvPairs; |
| |
| const _ImmutableMap._create(_ImmutableList keyValuePairs) |
| : _kvPairs = keyValuePairs; |
| |
| V operator [](Object key) { |
| // To preserve the key-value order of the map literal, the keys are |
| // not sorted. Need to do linear search or implement an additional |
| // lookup table. |
| for (int i = 0; i < _kvPairs.length - 1; i += 2) { |
| if (key == _kvPairs[i]) { |
| return _kvPairs[i + 1]; |
| } |
| } |
| return null; |
| } |
| |
| bool get isEmpty { |
| return _kvPairs.length == 0; |
| } |
| |
| bool get isNotEmpty => !isEmpty; |
| |
| int get length { |
| return _kvPairs.length ~/ 2; |
| } |
| |
| void forEach(void f(K key, V value)) { |
| for (int i = 0; i < _kvPairs.length; i += 2) { |
| f(_kvPairs[i], _kvPairs[i + 1]); |
| } |
| } |
| |
| Iterable<K> get keys { |
| return new _ImmutableMapKeyIterable<K>(this); |
| } |
| |
| Iterable<V> get values { |
| return new _ImmutableMapValueIterable<V>(this); |
| } |
| |
| bool containsKey(Object key) { |
| for (int i = 0; i < _kvPairs.length; i += 2) { |
| if (key == _kvPairs[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool containsValue(Object value) { |
| for (int i = 1; i < _kvPairs.length; i += 2) { |
| if (value == _kvPairs[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void operator []=(K key, V value) { |
| throw new UnsupportedError("Cannot set value in unmodifiable Map"); |
| } |
| |
| V putIfAbsent(K key, V ifAbsent()) { |
| throw new UnsupportedError("Cannot set value in unmodifiable Map"); |
| } |
| |
| void clear() { |
| throw new UnsupportedError("Cannot clear unmodifiable Map"); |
| } |
| |
| V remove(Object key) { |
| throw new UnsupportedError("Cannot remove from unmodifiable Map"); |
| } |
| |
| String toString() { |
| return Maps.mapToString(this); |
| } |
| } |
| class _ImmutableMapKeyIterable<E> extends EfficientLengthIterable<E> { |
| final _ImmutableMap _map; |
| _ImmutableMapKeyIterable(this._map); |
| |
| Iterator<E> get iterator { |
| return new _ImmutableMapKeyIterator<E>(_map); |
| } |
| |
| int get length => _map.length; |
| } |
| class _ImmutableMapValueIterable<E> extends EfficientLengthIterable<E> { |
| final _ImmutableMap _map; |
| _ImmutableMapValueIterable(this._map); |
| |
| Iterator<E> get iterator { |
| return new _ImmutableMapValueIterator<E>(_map); |
| } |
| |
| int get length => _map.length; |
| } |
| class _ImmutableMapKeyIterator<E> implements Iterator<E> { |
| _ImmutableMap _map; |
| int _index = -1; |
| E _current; |
| |
| _ImmutableMapKeyIterator(this._map); |
| |
| bool moveNext() { |
| int newIndex = _index + 1; |
| if (newIndex < _map.length) { |
| _index = newIndex; |
| _current = _map._kvPairs[newIndex * 2]; |
| return true; |
| } |
| _current = null; |
| _index = _map.length; |
| return false; |
| } |
| |
| E get current => _current; |
| } |
| class _ImmutableMapValueIterator<E> implements Iterator<E> { |
| _ImmutableMap _map; |
| int _index = -1; |
| E _current; |
| |
| _ImmutableMapValueIterator(this._map); |
| |
| bool moveNext() { |
| int newIndex = _index + 1; |
| if (newIndex < _map.length) { |
| _index = newIndex; |
| _current = _map._kvPairs[newIndex * 2 + 1]; |
| return true; |
| } |
| _current = null; |
| _index = _map.length; |
| return false; |
| } |
| |
| E get current => _current; |
| } |
| /* |
| abstract class _IntegerImplementation { |
| // The Dart class _Bigint extending _IntegerImplementation requires a |
| // default constructor. |
| |
| |
| num operator +(num other) { |
| var result = other._addFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._addFromInteger(this); |
| } |
| |
| num operator -(num other) { |
| var result = other._subFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._subFromInteger(this); |
| } |
| |
| num operator *(num other) { |
| var result = other._mulFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._mulFromInteger(this); |
| } |
| |
| num operator ~/(num other) { |
| if ((other is int) && (other == 0)) { |
| throw const IntegerDivisionByZeroException(); |
| } |
| var result = other._truncDivFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._truncDivFromInteger(this); |
| } |
| |
| double operator /(int other) { |
| return this.toDouble() / other.toDouble(); |
| } |
| |
| num operator %(num other) { |
| if ((other is int) && (other == 0)) { |
| throw const IntegerDivisionByZeroException(); |
| } |
| var result = other._moduloFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._moduloFromInteger(this); |
| } |
| |
| int operator -() { |
| return 0 - this; |
| } |
| |
| int operator &(int other) { |
| var result = other._bitAndFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._bitAndFromInteger(this); |
| } |
| |
| int operator |(int other) { |
| var result = other._bitOrFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._bitOrFromInteger(this); |
| } |
| |
| int operator ^(int other) { |
| var result = other._bitXorFromInteger(this); |
| if (result != null) return result; |
| return other._toBigint()._bitXorFromInteger(this); |
| } |
| |
| num remainder(num other) { |
| return other._remainderFromInteger(this); |
| } |
| |
| int _bitAndFromSmi(int other) native "Integer_bitAndFromInteger"; |
| int _bitAndFromInteger(int other) native "Integer_bitAndFromInteger"; |
| int _bitOrFromInteger(int other) native "Integer_bitOrFromInteger"; |
| int _bitXorFromInteger(int other) native "Integer_bitXorFromInteger"; |
| int _addFromInteger(int other) native "Integer_addFromInteger"; |
| int _subFromInteger(int other) native "Integer_subFromInteger"; |
| int _mulFromInteger(int other) native "Integer_mulFromInteger"; |
| int _truncDivFromInteger(int other) native "Integer_truncDivFromInteger"; |
| int _moduloFromInteger(int other) native "Integer_moduloFromInteger"; |
| int _remainderFromInteger(int other) { |
| return other - (other ~/ this) * this; |
| } |
| |
| |
| int operator >>(int other) { |
| var result = other._shrFromInt(this); |
| if (result != null) return result; |
| return other._toBigint()._shrFromInt(this); |
| } |
| |
| int operator <<(int other) { |
| var result = other._shlFromInt(this); |
| if (result != null) return result; |
| return other._toBigint()._shlFromInt(this); |
| } |
| |
| bool operator <(num other) { |
| return other > this; |
| } |
| |
| bool operator >(num other) { |
| return other._greaterThanFromInteger(this); |
| } |
| |
| bool operator >=(num other) { |
| return (this == other) || (this > other); |
| } |
| |
| bool operator <=(num other) { |
| return (this == other) || (this < other); |
| } |
| |
| bool _greaterThanFromInteger(int other) |
| native "Integer_greaterThanFromInteger"; |
| bool operator ==(other) { |
| if (other is num) { |
| return other._equalToInteger(this); |
| } |
| return false; |
| } |
| |
| bool _equalToInteger(int other) native "Integer_equalToInteger"; |
| |
| int abs() { |
| return this < 0 ? -this : this; |
| } |
| |
| int get sign { |
| return (this > 0) ? 1 : (this < 0) ? -1 : 0; |
| } |
| |
| bool get isEven => ((this & 1) == 0); |
| bool get isOdd => !isEven; |
| bool get isNaN => false; |
| bool get isNegative => this < 0; |
| bool get isInfinite => false; |
| bool get isFinite => true; |
| |
| int toUnsigned(int width) { |
| return this & ((1 << width) - 1); |
| } |
| |
| int toSigned(int width) { |
| // The value of binary number weights each bit by a power of two. The |
| // twos-complement value weights the sign bit negatively. We compute the |
| // value of the negative weighting by isolating the sign bit with the |
| // correct power of two weighting and subtracting it from the value of the |
| // lower bits. |
| int signMask = 1 << (width - 1); |
| return (this & (signMask - 1)) - (this & signMask); |
| } |
| |
| int compareTo(num other) { |
| const int EQUAL = 0, LESS = -1, GREATER = 1; |
| if (other is double) { |
| const int MAX_EXACT_INT_TO_DOUBLE = 9007199254740992; // 2^53. |
| const int MIN_EXACT_INT_TO_DOUBLE = -MAX_EXACT_INT_TO_DOUBLE; |
| double d = other; |
| if (d.isInfinite) { |
| return d == double.NEGATIVE_INFINITY ? GREATER : LESS; |
| } |
| if (d.isNaN) { |
| return LESS; |
| } |
| if (MIN_EXACT_INT_TO_DOUBLE <= this && this <= MAX_EXACT_INT_TO_DOUBLE) { |
| // Let the double implementation deal with -0.0. |
| return -(d.compareTo(this.toDouble())); |
| } else { |
| // If abs(other) > MAX_EXACT_INT_TO_DOUBLE, then other has an integer |
| // value (no bits below the decimal point). |
| other = d.toInt(); |
| } |
| } |
| if (this < other) { |
| return LESS; |
| } else if (this > other) { |
| return GREATER; |
| } else { |
| return EQUAL; |
| } |
| } |
| |
| int round() { |
| return this; |
| } |
| |
| int floor() { |
| return this; |
| } |
| |
| int ceil() { |
| return this; |
| } |
| |
| int truncate() { |
| return this; |
| } |
| |
| double roundToDouble() { |
| return this.toDouble(); |
| } |
| |
| double floorToDouble() { |
| return this.toDouble(); |
| } |
| |
| double ceilToDouble() { |
| return this.toDouble(); |
| } |
| |
| double truncateToDouble() { |
| return this.toDouble(); |
| } |
| |
| num clamp(num lowerLimit, num upperLimit) { |
| if (lowerLimit is! num) { |
| throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number"); |
| } |
| if (upperLimit is! num) { |
| throw new ArgumentError.value(upperLimit, "upperLimit", "not a number"); |
| } |
| |
| // Special case for integers. |
| if (lowerLimit is int && upperLimit is int && lowerLimit <= upperLimit) { |
| if (this < lowerLimit) return lowerLimit; |
| if (this > upperLimit) return upperLimit; |
| return this; |
| } |
| // Generic case involving doubles, and invalid integer ranges. |
| if (lowerLimit.compareTo(upperLimit) > 0) { |
| throw new ArgumentError(lowerLimit); |
| } |
| if (lowerLimit.isNaN) return lowerLimit; |
| // Note that we don't need to care for -0.0 for the lower limit. |
| if (this < lowerLimit) return lowerLimit; |
| if (this.compareTo(upperLimit) > 0) return upperLimit; |
| return this; |
| } |
| |
| int toInt() { |
| return this; |
| } |
| |
| double toDouble() { |
| return new _Double.fromInteger(this); |
| } |
| |
| int _toBigint() { |
| return this; |
| } |
| |
| num _toBigintOrDouble() { |
| return _toBigint(); |
| } |
| |
| String toStringAsFixed(int fractionDigits) { |
| return this.toDouble().toStringAsFixed(fractionDigits); |
| } |
| |
| String toStringAsExponential([int fractionDigits]) { |
| return this.toDouble().toStringAsExponential(fractionDigits); |
| } |
| |
| String toStringAsPrecision(int precision) { |
| return this.toDouble().toStringAsPrecision(precision); |
| } |
| |
| static const _digits = "0123456789abcdefghijklmnopqrstuvwxyz"; |
| |
| String toRadixString(int radix) { |
| if (radix < 2 || 36 < radix) { |
| throw new RangeError.range(radix, 2, 36, "radix"); |
| } |
| if (radix & (radix - 1) == 0) { |
| return _toPow2String(radix); |
| } |
| if (radix == 10) return this.toString(); |
| final bool isNegative = this < 0; |
| int value = isNegative ? -this : this; |
| List temp = new List(); |
| do { |
| int digit = value % radix; |
| value ~/= radix; |
| temp.add(_digits.codeUnitAt(digit)); |
| } while (value > 0); |
| if (isNegative) temp.add(0x2d); // '-'. |
| |
| _OneByteString string = _OneByteString._allocate(temp.length); |
| for (int i = 0, j = temp.length; j > 0; i++) { |
| string._setAt(i, temp[--j]); |
| } |
| return string; |
| } |
| |
| String _toPow2String(int radix) { |
| int value = this; |
| if (value == 0) return "0"; |
| assert(radix & (radix - 1) == 0); |
| var negative = value < 0; |
| var bitsPerDigit = radix.bitLength - 1; |
| var length = 0; |
| if (negative) { |
| value = -value; |
| length = 1; |
| } |
| // Integer division, rounding up, to find number of _digits. |
| length += (value.bitLength + bitsPerDigit - 1) ~/ bitsPerDigit; |
| _OneByteString string = _OneByteString._allocate(length); |
| string._setAt(0, 0x2d); // '-'. Is overwritten if not negative. |
| var mask = radix - 1; |
| do { |
| string._setAt(--length, _digits.codeUnitAt(value & mask)); |
| value >>= bitsPerDigit; |
| } while (value > 0); |
| return string; |
| } |
| |
| // Returns pow(this, e) % m. |
| int modPow(int e, int m) { |
| if (e is! int) { |
| throw new ArgumentError.value(e, "exponent", "not an integer"); |
| } |
| if (m is! int) { |
| throw new ArgumentError.value(m, "modulus", "not an integer"); |
| } |
| if (e < 0) throw new RangeError.range(e, 0, null, "exponent"); |
| if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); |
| if (e == 0) return 1; |
| int b = this; |
| if (b < 0 || b > m) { |
| b %= m; |
| } |
| int r = 1; |
| while (e > 0) { |
| if (e.isOdd) { |
| r = (r * b) % m; |
| } |
| e >>= 1; |
| b = (b * b) % m; |
| } |
| return r; |
| } |
| |
| // If inv is false, returns gcd(x, y). |
| // If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1. |
| // If inv is true and gcd(x, y) != 1, throws Exception("Not coprime"). |
| static int _binaryGcd(int x, int y, bool inv) { |
| int s = 0; |
| if (!inv) { |
| while (x.isEven && y.isEven) { |
| x >>= 1; |
| y >>= 1; |
| s++; |
| } |
| if (y.isOdd) { |
| var t = x; |
| x = y; |
| y = t; |
| } |
| } |
| final bool ac = x.isEven; |
| int u = x; |
| int v = y; |
| int a = 1, b = 0, c = 0, d = 1; |
| do { |
| while (u.isEven) { |
| u >>= 1; |
| if (ac) { |
| if (!a.isEven || !b.isEven) { |
| a += y; |
| b -= x; |
| } |
| a >>= 1; |
| } else if (!b.isEven) { |
| b -= x; |
| } |
| b >>= 1; |
| } |
| while (v.isEven) { |
| v >>= 1; |
| if (ac) { |
| if (!c.isEven || !d.isEven) { |
| c += y; |
| d -= x; |
| } |
| c >>= 1; |
| } else if (!d.isEven) { |
| d -= x; |
| } |
| d >>= 1; |
| } |
| if (u >= v) { |
| u -= v; |
| if (ac) a -= c; |
| b -= d; |
| } else { |
| v -= u; |
| if (ac) c -= a; |
| d -= b; |
| } |
| } while (u != 0); |
| if (!inv) return v << s; |
| if (v != 1) { |
| throw new Exception("Not coprime"); |
| } |
| if (d < 0) { |
| d += x; |
| if (d < 0) d += x; |
| } else if (d > x) { |
| d -= x; |
| if (d > x) d -= x; |
| } |
| return d; |
| } |
| |
| // Returns 1/this % m, with m > 0. |
| int modInverse(int m) { |
| if (m is! int) { |
| throw new ArgumentError.value(m, "modulus", "not an integer"); |
| } |
| if (m <= 0) throw new RangeError.range(m, 1, null, "modulus"); |
| if (m == 1) return 0; |
| int t = this; |
| if ((t < 0) || (t >= m)) t %= m; |
| if (t == 1) return 1; |
| if ((t == 0) || (t.isEven && m.isEven)) { |
| throw new Exception("Not coprime"); |
| } |
| return _binaryGcd(m, t, true); |
| } |
| |
| // Returns gcd of abs(this) and abs(other). |
| int gcd(int other) { |
| if (other is! int) { |
| throw new ArgumentError.value(other, "other", "not an integer"); |
| } |
| int x = this.abs(); |
| int y = other.abs(); |
| if (x == 0) return y; |
| if (y == 0) return x; |
| if ((x == 1) || (y == 1)) return 1; |
| return _binaryGcd(x, y, false); |
| } |
| }*/ |
| class _Integer implements int { |
| factory _Integer._uninstantiable() { |
| throw new UnsupportedError("_Smi can only be allocated by the VM"); |
| } |
| int get hashCode => this; |
| int get _identityHashCode => this; |
| int operator ~() native "Smi_bitNegate"; |
| int get bitLength native "Smi_bitLength"; |
| |
| //int operator &(int other) => other._bitAndFromSmi(this); |
| |
| //int _bitAndFromSmi(int other) native "Smi_bitAndFromSmi"; |
| //int _shrFromInt(int other) native "Smi_shrFromInt"; |
| //int _shlFromInt(int other) native "Smi_shlFromInt"; |
| |
| /** |
| * The digits of '00', '01', ... '99' as a single array. |
| * |
| * Get the digits of `n`, with `0 <= n < 100`, as |
| * `_digitTable[n * 2]` and `_digitTable[n * 2 + 1]`. |
| */ |
| static final Uint8List _digitTable = buildTable(); |
| |
| static Uint8List buildTable() { |
| final Uint8List table = new Uint8List(200); |
| int i = 0; |
| table[i++] = 0x30; |
| table[i++] = 0x30; |
| table[i++] = 0x30; |
| table[i++] = 0x31; |
| table[i++] = 0x30; |
| table[i++] = 0x32; |
| table[i++] = 0x30; |
| table[i++] = 0x33; |
| table[i++] = 0x30; |
| table[i++] = 0x34; |
| table[i++] = 0x30; |
| table[i++] = 0x35; |
| table[i++] = 0x30; |
| table[i++] = 0x36; |
| table[i++] = 0x30; |
| table[i++] = 0x37; |
| table[i++] = 0x30; |
| table[i++] = 0x38; |
| table[i++] = 0x30; |
| table[i++] = 0x39; |
| table[i++] = 0x31; |
| table[i++] = 0x30; |
| table[i++] = 0x31; |
| table[i++] = 0x31; |
| table[i++] = 0x31; |
| table[i++] = 0x32; |
| table[i++] = 0x31; |
| table[i++] = 0x33; |
| table[i++] = 0x31; |
| table[i++] = 0x34; |
| table[i++] = 0x31; |
| table[i++] = 0x35; |
| table[i++] = 0x31; |
| table[i++] = 0x36; |
| table[i++] = 0x31; |
| table[i++] = 0x37; |
| table[i++] = 0x31; |
| table[i++] = 0x38; |
| table[i++] = 0x31; |
| table[i++] = 0x39; |
| table[i++] = 0x32; |
| table[i++] = 0x30; |
| table[i++] = 0x32; |
| table[i++] = 0x31; |
| table[i++] = 0x32; |
| table[i++] = 0x32; |
| table[i++] = 0x32; |
| table[i++] = 0x33; |
| table[i++] = 0x32; |
| table[i++] = 0x34; |
| table[i++] = 0x32; |
| table[i++] = 0x35; |
| table[i++] = 0x32; |
| table[i++] = 0x36; |
| table[i++] = 0x32; |
| table[i++] = 0x37; |
| table[i++] = 0x32; |
| table[i++] = 0x38; |
| table[i++] = 0x32; |
| table[i++] = 0x39; |
| table[i++] = 0x33; |
| table[i++] = 0x30; |
| table[i++] = 0x33; |
| table[i++] = 0x31; |
| table[i++] = 0x33; |
| table[i++] = 0x32; |
| table[i++] = 0x33; |
| table[i++] = 0x33; |
| table[i++] = 0x33; |
| table[i++] = 0x34; |
| table[i++] = 0x33; |
| table[i++] = 0x35; |
| table[i++] = 0x33; |
| table[i++] = 0x36; |
| table[i++] = 0x33; |
| table[i++] = 0x37; |
| table[i++] = 0x33; |
| table[i++] = 0x38; |
| table[i++] = 0x33; |
| table[i++] = 0x39; |
| table[i++] = 0x34; |
| table[i++] = 0x30; |
| table[i++] = 0x34; |
| table[i++] = 0x31; |
| table[i++] = 0x34; |
| table[i++] = 0x32; |
| table[i++] = 0x34; |
| table[i++] = 0x33; |
| table[i++] = 0x34; |
| table[i++] = 0x34; |
| table[i++] = 0x34; |
| table[i++] = 0x35; |
| table[i++] = 0x34; |
| table[i++] = 0x36; |
| table[i++] = 0x34; |
| table[i++] = 0x37; |
| table[i++] = 0x34; |
| table[i++] = 0x38; |
| table[i++] = 0x34; |
| table[i++] = 0x39; |
| table[i++] = 0x35; |
| table[i++] = 0x30; |
| table[i++] = 0x35; |
| table[i++] = 0x31; |
| table[i++] = 0x35; |
| table[i++] = 0x32; |
| table[i++] = 0x35; |
| table[i++] = 0x33; |
| table[i++] = 0x35; |
| table[i++] = 0x34; |
| table[i++] = 0x35; |
| table[i++] = 0x35; |
| table[i++] = 0x35; |
| table[i++] = 0x36; |
| table[i++] = 0x35; |
| table[i++] = 0x37; |
| table[i++] = 0x35; |
| table[i++] = 0x38; |
| table[i++] = 0x35; |
| table[i++] = 0x39; |
| table[i++] = 0x36; |
| table[i++] = 0x30; |
| table[i++] = 0x36; |
| table[i++] = 0x31; |
| table[i++] = 0x36; |
| table[i++] = 0x32; |
| table[i++] = 0x36; |
| table[i++] = 0x33; |
| table[i++] = 0x36; |
| table[i++] = 0x34; |
| table[i++] = 0x36; |
| table[i++] = 0x35; |
| table[i++] = 0x36; |
| table[i++] = 0x36; |
| table[i++] = 0x36; |
| table[i++] = 0x37; |
| table[i++] = 0x36; |
| table[i++] = 0x38; |
| table[i++] = 0x36; |
| table[i++] = 0x39; |
| table[i++] = 0x37; |
| table[i++] = 0x30; |
| table[i++] = 0x37; |
| table[i++] = 0x31; |
| table[i++] = 0x37; |
| table[i++] = 0x32; |
| table[i++] = 0x37; |
| table[i++] = 0x33; |
| table[i++] = 0x37; |
| table[i++] = 0x34; |
| table[i++] = 0x37; |
| table[i++] = 0x35; |
| table[i++] = 0x37; |
| table[i++] = 0x36; |
| table[i++] = 0x37; |
| table[i++] = 0x37; |
| table[i++] = 0x37; |
| table[i++] = 0x38; |
| table[i++] = 0x37; |
| table[i++] = 0x39; |
| table[i++] = 0x38; |
| table[i++] = 0x30; |
| table[i++] = 0x38; |
| table[i++] = 0x31; |
| table[i++] = 0x38; |
| table[i++] = 0x32; |
| table[i++] = 0x38; |
| table[i++] = 0x33; |
| table[i++] = 0x38; |
| table[i++] = 0x34; |
| table[i++] = 0x38; |
| table[i++] = 0x35; |
| table[i++] = 0x38; |
| table[i++] = 0x36; |
| table[i++] = 0x38; |
| table[i++] = 0x37; |
| table[i++] = 0x38; |
| table[i++] = 0x38; |
| table[i++] = 0x38; |
| table[i++] = 0x39; |
| table[i++] = 0x39; |
| table[i++] = 0x30; |
| table[i++] = 0x39; |
| table[i++] = 0x31; |
| table[i++] = 0x39; |
| table[i++] = 0x32; |
| table[i++] = 0x39; |
| table[i++] = 0x33; |
| table[i++] = 0x39; |
| table[i++] = 0x34; |
| table[i++] = 0x39; |
| table[i++] = 0x35; |
| table[i++] = 0x39; |
| table[i++] = 0x36; |
| table[i++] = 0x39; |
| table[i++] = 0x37; |
| table[i++] = 0x39; |
| table[i++] = 0x38; |
| table[i++] = 0x39; |
| table[i++] = 0x39; |
| return table; |
| } |
| |
| /** |
| * Result of int.toString for -99, -98, ..., 98, 99. |
| */ |
| static const List<String> _smallLookupTable = const <String>[ |
| "-99", "-98", "-97", "-96", "-95", "-94", "-93", "-92", "-91", "-90", // |
| "-89", "-88", "-87", "-86", "-85", "-84", "-83", "-82", "-81", "-80", // |
| "-79", "-78", "-77", "-76", "-75", "-74", "-73", "-72", "-71", "-70", // |
| "-69", "-68", "-67", "-66", "-65", "-64", "-63", "-62", "-61", "-60", // |
| "-59", "-58", "-57", "-56", "-55", "-54", "-53", "-52", "-51", "-50", // |
| "-49", "-48", "-47", "-46", "-45", "-44", "-43", "-42", "-41", "-40", // |
| "-39", "-38", "-37", "-36", "-35", "-34", "-33", "-32", "-31", "-30", // |
| "-29", "-28", "-27", "-26", "-25", "-24", "-23", "-22", "-21", "-20", // |
| "-19", "-18", "-17", "-16", "-15", "-14", "-13", "-12", "-11", "-10", // |
| "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", "0", // |
| "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", // |
| "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", // |
| "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", // |
| "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", // |
| "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", // |
| "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", // |
| "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", // |
| "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", // |
| "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", // |
| "91", "92", "93", "94", "95", "96", "97", "98", "99" // |
| ]; |
| |
| // Powers of 10 above 1000000 are indistinguishable by eye. |
| static const int _POW_10_7 = 10000000; |
| static const int _POW_10_8 = 100000000; |
| static const int _POW_10_9 = 1000000000; |
| |
| // Find the number of decimal digits in a positive smi. |
| // Never called with numbers < 100. These are handled before calling. |
| static int _positiveBase10Length(int smi) { |
| // A positive smi has length <= 19 if 63-bit, <=10 if 31-bit. |
| // Avoid comparing a 31-bit smi to a non-smi. |
| if (smi < 1000) return 3; |
| if (smi < 10000) return 4; |
| if (smi < _POW_10_7) { |
| if (smi < 100000) return 5; |
| if (smi < 1000000) return 6; |
| return 7; |
| } |
| if (smi < _POW_10_8) return 8; |
| if (smi < _POW_10_9) return 9; |
| smi = smi ~/ _POW_10_9; |
| // Handle numbers < 100 before calling recursively. |
| if (smi < 10) return 10; |
| if (smi < 100) return 11; |
| return 9 + _positiveBase10Length(smi); |
| } |
| |
| String toString() { |
| if (this < 100 && this > -100) return _smallLookupTable[this + 99]; |
| if (this < 0) return _negativeToString(this); |
| // Inspired by Andrei Alexandrescu: "Three Optimization Tips for C++" |
| // Avoid expensive remainder operation by doing it on more than |
| // one digit at a time. |
| const int DIGIT_ZERO = 0x30; |
| int length = _positiveBase10Length(this); |
| _OneByteString result = _OneByteString._allocate(length); |
| int index = length - 1; |
| int smi = this; |
| do { |
| // Two digits at a time. |
| var twoDigits = smi.remainder(100); |
| smi = smi ~/ 100; |
| int digitIndex = twoDigits * 2; |
| result._setAt(index, _digitTable[digitIndex + 1]); |
| result._setAt(index - 1, _digitTable[digitIndex]); |
| index -= 2; |
| } while (smi >= 100); |
| if (smi < 10) { |
| // Character code for '0'. |
| result._setAt(index, DIGIT_ZERO + smi); |
| } else { |
| // No remainder for this case. |
| int digitIndex = smi * 2; |
| result._setAt(index, _digitTable[digitIndex + 1]); |
| result._setAt(index - 1, _digitTable[digitIndex]); |
| } |
| return result; |
| } |
| |
| // Find the number of decimal digits in a negative smi. |
| // Never called with numbers > -100. These are handled before calling. |
| static int _negativeBase10Length(int negSmi) { |
| // A negative smi has length <= 19 if 63-bit, <=10 if 31-bit. |
| // Avoid comparing a 31-bit smi to a non-smi. |
| if (negSmi > -1000) return 3; |
| if (negSmi > -10000) return 4; |
| if (negSmi > -_POW_10_7) { |
| if (negSmi > -100000) return 5; |
| if (negSmi > -1000000) return 6; |
| return 7; |
| } |
| if (negSmi > -_POW_10_8) return 8; |
| if (negSmi > -_POW_10_9) return 9; |
| negSmi = negSmi ~/ _POW_10_9; |
| // Handle numbers > -100 before calling recursively. |
| if (negSmi > -10) return 10; |
| if (negSmi > -100) return 11; |
| return 9 + _negativeBase10Length(negSmi); |
| } |
| |
| // Convert a negative smi to a string. |
| // Doesn't negate the smi to avoid negating the most negative smi, which |
| // would become a non-smi. |
| static String _negativeToString(int negSmi) { |
| // Character code for '-' |
| const int MINUS_SIGN = 0x2d; |
| // Character code for '0'. |
| const int DIGIT_ZERO = 0x30; |
| if (negSmi > -10) { |
| return _OneByteString._allocate(2) |
| .._setAt(0, MINUS_SIGN) |
| .._setAt(1, DIGIT_ZERO - negSmi); |
| } |
| if (negSmi > -100) { |
| int digitIndex = 2 * -negSmi; |
| return _OneByteString._allocate(3) |
| .._setAt(0, MINUS_SIGN) |
| .._setAt(1, _digitTable[digitIndex]) |
| .._setAt(2, _digitTable[digitIndex + 1]); |
| } |
| // Number of digits, not including minus. |
| int digitCount = _negativeBase10Length(negSmi); |
| _OneByteString result = _OneByteString._allocate(digitCount + 1); |
| result._setAt(0, MINUS_SIGN); // '-'. |
| int index = digitCount; |
| do { |
| var twoDigits = negSmi.remainder(100); |
| negSmi = negSmi ~/ 100; |
| int digitIndex = -twoDigits * 2; |
| result._setAt(index, _digitTable[digitIndex + 1]); |
| result._setAt(index - 1, _digitTable[digitIndex]); |
| index -= 2; |
| } while (negSmi <= -100); |
| if (negSmi > -10) { |
| result._setAt(index, DIGIT_ZERO - negSmi); |
| } else { |
| // No remainder necessary for this case. |
| int digitIndex = -negSmi * 2; |
| result._setAt(index, _digitTable[digitIndex + 1]); |
| result._setAt(index - 1, _digitTable[digitIndex]); |
| } |
| return result; |
| } |
| } |
| |
| |
| class _InvocationMirror implements Invocation { |
| // Constants describing the invocation type. |
| // _FIELD cannot be generated by regular invocation mirrors. |
| static const int _METHOD = 0; |
| static const int _GETTER = 1; |
| static const int _SETTER = 2; |
| static const int _FIELD = 3; |
| static const int _LOCAL_VAR = 4; |
| static const int _TYPE_SHIFT = 0; |
| static const int _TYPE_BITS = 3; |
| static const int _TYPE_MASK = (1 << _TYPE_BITS) - 1; |
| |
| // These values, except _DYNAMIC and _SUPER, are only used when throwing |
| // NoSuchMethodError for compile-time resolution failures. |
| static const int _DYNAMIC = 0; |
| static const int _SUPER = 1; |
| static const int _STATIC = 2; |
| static const int _CONSTRUCTOR = 3; |
| static const int _TOP_LEVEL = 4; |
| static const int _CALL_SHIFT = _TYPE_BITS; |
| static const int _CALL_BITS = 3; |
| static const int _CALL_MASK = (1 << _CALL_BITS) - 1; |
| |
| // Internal representation of the invocation mirror. |
| final String _functionName; |
| final List<Object> _argumentsDescriptor; |
| final List<Object> _arguments; |
| final bool _isSuperInvocation; |
| |
| // External representation of the invocation mirror; populated on demand. |
| Symbol _memberName; |
| int _type = -1; |
| List<Object> _positionalArguments; |
| Map<Symbol, Object> _namedArguments; |
| |
| void _setMemberNameAndType() { |
| if (_functionName.startsWith("get:")) { |
| _type = _GETTER; |
| _memberName = new internal.Symbol.unvalidated(_functionName.substring(4)); |
| } else if (_functionName.startsWith("set:")) { |
| _type = _SETTER; |
| _memberName = |
| new internal.Symbol.unvalidated(_functionName.substring(4) + "="); |
| } else { |
| _type = _isSuperInvocation ? (_SUPER << _CALL_SHIFT) | _METHOD : _METHOD; |
| _memberName = new internal.Symbol.unvalidated(_functionName); |
| } |
| } |
| |
| Symbol get memberName { |
| if (_memberName == null) { |
| _setMemberNameAndType(); |
| } |
| return _memberName; |
| } |
| |
| List get positionalArguments { |
| if (_positionalArguments == null) { |
| int numPositionalArguments = _argumentsDescriptor[1] as int; |
| // Don't count receiver. |
| if (numPositionalArguments == 1) { |
| return _positionalArguments = const <Object>[]; |
| } |
| // Exclude receiver. |
| _positionalArguments = |
| new _ImmutableList<Object>._from(_arguments, 1, numPositionalArguments - 1); |
| } |
| return _positionalArguments; |
| } |
| |
| Map<Symbol, Object> get namedArguments { |
| if (_namedArguments == null) { |
| int numArguments = (_argumentsDescriptor[0] as int) - 1; // Exclude receiver. |
| int numPositionalArguments = (_argumentsDescriptor[1] as int) - 1; |
| int numNamedArguments = numArguments - numPositionalArguments; |
| if (numNamedArguments == 0) { |
| return _namedArguments = const <Symbol, Object>{}; |
| } |
| _namedArguments = new Map<Symbol, Object>(); |
| for (int i = 0; i < numNamedArguments; i++) { |
| String arg_name = _argumentsDescriptor[2 + 2 * i]; |
| var arg_value = _arguments[_argumentsDescriptor[3 + 2 * i] as int]; |
| _namedArguments[new internal.Symbol.unvalidated(arg_name)] = arg_value; |
| } |
| _namedArguments = new Map<Symbol, Object>.unmodifiable(_namedArguments); |
| } |
| return _namedArguments; |
| } |
| |
| bool get isMethod { |
| if (_type == -1) { |
| _setMemberNameAndType(); |
| } |
| return (_type & _TYPE_MASK) == _METHOD; |
| } |
| |
| bool get isAccessor { |
| if (_type == -1) { |
| _setMemberNameAndType(); |
| } |
| return (_type & _TYPE_MASK) != _METHOD; |
| } |
| |
| bool get isGetter { |
| if (_type == -1) { |
| _setMemberNameAndType(); |
| } |
| return (_type & _TYPE_MASK) == _GETTER; |
| } |
| |
| bool get isSetter { |
| if (_type == -1) { |
| _setMemberNameAndType(); |
| } |
| return (_type & _TYPE_MASK) == _SETTER; |
| } |
| |
| _InvocationMirror(this._functionName, this._argumentsDescriptor, |
| this._arguments, this._isSuperInvocation); |
| |
| static _allocateInvocationMirror(String functionName, |
| List argumentsDescriptor, List arguments, bool isSuperInvocation) { |
| return new _InvocationMirror( |
| functionName, argumentsDescriptor, arguments, isSuperInvocation); |
| } |
| } |
| /* |
| class _LibraryPrefix { |
| bool _load() native "LibraryPrefix_load"; |
| Object _loadError() native "LibraryPrefix_loadError"; |
| bool isLoaded() native "LibraryPrefix_isLoaded"; |
| bool _invalidateDependentCode() |
| native "LibraryPrefix_invalidateDependentCode"; |
| |
| loadLibrary() { |
| for (int i = 0; i < _outstandingLoadRequests.length; i++) { |
| if (_outstandingLoadRequests[i][0] == this) { |
| return _outstandingLoadRequests[i][1].future; |
| } |
| } |
| |
| var completer = new Completer<bool>(); |
| var pair = new List(); |
| pair.add(this); |
| pair.add(completer); |
| _outstandingLoadRequests.add(pair); |
| Timer.run(() { |
| var hasCompleted = this._load(); |
| // Loading can complete immediately, for example when the same |
| // library has been loaded eagerly or through another deferred |
| // prefix. If that is the case, we must invalidate the dependent |
| // code and complete the future now since there will be no callback |
| // from the VM. |
| if (hasCompleted && !completer.isCompleted) { |
| _invalidateDependentCode(); |
| completer.complete(true); |
| _outstandingLoadRequests.remove(pair); |
| } |
| }); |
| return completer.future; |
| } |
| } |
| var _outstandingLoadRequests = new List<List>(); |
| _completeDeferredLoads() { |
| // Determine which outstanding load requests have completed and complete |
| // their completer (with an error or true). For outstanding load requests |
| // which have not completed, remember them for next time in |
| // stillOutstandingLoadRequests. |
| var stillOutstandingLoadRequests = new List<List>(); |
| var completedLoadRequests = new List<List>(); |
| |
| // Make a copy of the outstandingRequests because the call to _load below |
| // may recursively trigger another call to |_completeDeferredLoads|, which |
| // can cause |_outstandingLoadRequests| to be modified. |
| var outstandingRequests = _outstandingLoadRequests.toList(); |
| for (int i = 0; i < outstandingRequests.length; i++) { |
| var prefix = outstandingRequests[i][0]; |
| var completer = outstandingRequests[i][1]; |
| var error = prefix._loadError(); |
| if (completer.isCompleted) { |
| // Already completed. Skip. |
| continue; |
| } |
| if (error != null) { |
| completer.completeError(error); |
| } else if (prefix._load()) { |
| prefix._invalidateDependentCode(); |
| completer.complete(true); |
| } else { |
| stillOutstandingLoadRequests.add(outstandingRequests[i]); |
| } |
| } |
| _outstandingLoadRequests = stillOutstandingLoadRequests; |
| }*/ |
| class _RegExpHashKey extends LinkedListEntry<_RegExpHashKey> { |
| final String pattern; |
| final bool multiLine; |
| final bool caseSensitive; |
| |
| _RegExpHashKey(this.pattern, this.multiLine, this.caseSensitive); |
| |
| int get hashCode => pattern.hashCode; |
| bool operator ==(_RegExpHashKey that) { |
| return (this.pattern == that.pattern) && |
| (this.multiLine == that.multiLine) && |
| (this.caseSensitive == that.caseSensitive); |
| } |
| } |
| class _RegExpHashValue { |
| final _RegExp regexp; |
| final _RegExpHashKey key; |
| |
| _RegExpHashValue(this.regexp, this.key); |
| } |
| class _RegExpMatch implements Match { |
| _RegExpMatch(this._regexp, this.input, this._match); |
| |
| int get start => _start(0); |
| int get end => _end(0); |
| |
| int _start(int groupIdx) { |
| return _match[(groupIdx * _MATCH_PAIR)]; |
| } |
| |
| int _end(int groupIdx) { |
| return _match[(groupIdx * _MATCH_PAIR) + 1]; |
| } |
| |
| String group(int groupIdx) { |
| if (groupIdx < 0 || groupIdx > _regexp._groupCount) { |
| throw new RangeError.value(groupIdx); |
| } |
| int startIndex = _start(groupIdx); |
| int endIndex = _end(groupIdx); |
| if (startIndex == -1) { |
| assert(endIndex == -1); |
| return null; |
| } |
| return (input as _StringBase)._substringUnchecked(startIndex, endIndex); |
| } |
| |
| String operator [](int groupIdx) { |
| return this.group(groupIdx); |
| } |
| |
| List<String> groups(List<int> groupsSpec) { |
| var groupsList = new List<String>(groupsSpec.length); |
| for (int i = 0; i < groupsSpec.length; i++) { |
| groupsList[i] = group(groupsSpec[i]); |
| } |
| return groupsList; |
| } |
| |
| int get groupCount => _regexp._groupCount; |
| |
| Pattern get pattern => _regexp; |
| |
| final _RegExp _regexp; |
| final String input; |
| final List<int> _match; |
| static const int _MATCH_PAIR = 2; |
| } |
| class _RegExp implements RegExp { |
| factory _RegExp(String pattern, |
| {bool multiLine: false, |
| bool caseSensitive: true}) native "RegExp_factory"; |
| |
| Match firstMatch(String str) { |
| if (str is! String) throw new ArgumentError(str); |
| List match = _ExecuteMatch(str, 0); |
| if (match == null) { |
| return null; |
| } |
| return new _RegExpMatch(this, str, match); |
| } |
| |
| Iterable<Match> allMatches(String string, [int start = 0]) { |
| if (string is! String) throw new ArgumentError(string); |
| if (0 > start || start > string.length) { |
| throw new RangeError.range(start, 0, string.length); |
| } |
| return new _AllMatchesIterable(this, string, start); |
| } |
| |
| Match matchAsPrefix(String string, [int start = 0]) { |
| if (string is! String) throw new ArgumentError(string); |
| if (start < 0 || start > string.length) { |
| throw new RangeError.range(start, 0, string.length); |
| } |
| List<int> list = _ExecuteMatchSticky(string, start); |
| if (list == null) return null; |
| return new _RegExpMatch(this, string, list); |
| } |
| |
| bool hasMatch(String str) { |
| if (str is! String) throw new ArgumentError(str); |
| List match = _ExecuteMatch(str, 0); |
| return (match == null) ? false : true; |
| } |
| |
| String stringMatch(String str) { |
| if (str is! String) throw new ArgumentError(str); |
| List match = _ExecuteMatch(str, 0); |
| if (match == null) { |
| return null; |
| } |
| return str._substringUnchecked(match[0], match[1]); |
| } |
| |
| String get pattern native "RegExp_getPattern"; |
| |
| bool get isMultiLine native "RegExp_getIsMultiLine"; |
| |
| bool get isCaseSensitive native "RegExp_getIsCaseSensitive"; |
| |
| int get _groupCount native "RegExp_getGroupCount"; |
| |
| // Byte map of one byte characters with a 0xff if the character is a word |
| // character (digit, letter or underscore) and 0x00 otherwise. |
| // Used by generated RegExp code. |
| static const List<int> _wordCharacterMap = const <int>[ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // '0' - '7' |
| 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // '8' - '9' |
| |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'A' - 'G' |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'H' - 'O' |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'P' - 'W' |
| 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, // 'X' - 'Z', '_' |
| |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'a' - 'g' |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'h' - 'o' |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 'p' - 'w' |
| 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, // 'x' - 'z' |
| // Latin-1 range |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| ]; |
| |
| List _ExecuteMatch(String str, int start_index) native "RegExp_ExecuteMatch"; |
| |
| List _ExecuteMatchSticky(String str, int start_index) |
| native "RegExp_ExecuteMatchSticky"; |
| } |
| class _WeakProperty { |
| factory _WeakProperty(key, value) => _new(key, value); |
| |
| get key => _getKey(); |
| get value => _getValue(); |
| set value(value) => _setValue(value); |
| |
| static _WeakProperty _new(key, value) native "WeakProperty_new"; |
| |
| _getKey() native "WeakProperty_getKey"; |
| _getValue() native "WeakProperty_getValue"; |
| _setValue(value) native "WeakProperty_setValue"; |
| } |
| class _AllMatchesIterator implements Iterator<Match> { |
| final String _str; |
| int _nextIndex; |
| _RegExp _re; |
| Match _current; |
| |
| _AllMatchesIterator(this._re, this._str, this._nextIndex); |
| |
| Match get current => _current; |
| |
| bool moveNext() { |
| if (_re == null) return false; // Cleared after a failed match. |
| if (_nextIndex <= _str.length) { |
| var match = _re._ExecuteMatch(_str, _nextIndex); |
| if (match != null) { |
| _current = new _RegExpMatch(_re, _str, match); |
| _nextIndex = _current.end; |
| if (_nextIndex == _current.start) { |
| // Zero-width match. Advance by one more. |
| _nextIndex++; |
| } |
| return true; |
| } |
| } |
| _current = null; |
| _re = null; |
| return false; |
| } |
| } |
| class _StackTrace implements StackTrace { |
| // toString() is overridden on the C++ side. |
| } |
| const int _maxAscii = 0x7f; |
| const int _maxLatin1 = 0xff; |
| const int _maxUtf16 = 0xffff; |
| const int _maxUnicode = 0x10ffff; |
| /** |
| * [_StringBase] contains common methods used by concrete String |
| * implementations, e.g., _OneByteString. |
| */ |
| abstract class _StringBase implements String { |
| // Constants used by replaceAll encoding of string slices between matches. |
| // A string slice (start+length) is encoded in a single Smi to save memory |
| // overhead in the common case. |
| // We use fewer bits for length (11 bits) than for the start index (19+ bits). |
| // For long strings, it's possible to have many large indices, |
| // but it's unlikely to have many long lengths since slices don't overlap. |
| // If there are few matches in a long string, then there are few long slices, |
| // and if there are many matches, there'll likely be many short slices. |
| // |
| // Encoding is: 0((start << _lengthBits) | length) |
| |
| // Number of bits used by length. |
| // This is the shift used to encode and decode the start index. |
| static const int _lengthBits = 11; |
| // The maximal allowed length value in an encoded slice. |
| static const int _maxLengthValue = (1 << _lengthBits) - 1; |
| // Mask of length in encoded smi value. |
| static const int _lengthMask = _maxLengthValue; |
| static const int _startBits = _maxUnsignedSmiBits - _lengthBits; |
| // Maximal allowed start index value in an encoded slice. |
| static const int _maxStartValue = (1 << _startBits) - 1; |
| // We pick 30 as a safe lower bound on available bits in a negative smi. |
| // TODO(lrn): Consider allowing more bits for start on 64-bit systems. |
| static const int _maxUnsignedSmiBits = 30; |
| |
| // For longer strings, calling into C++ to create the result of a |
| // [replaceAll] is faster than [_joinReplaceAllOneByteResult]. |
| // TODO(lrn): See if this limit can be tweaked. |
| static const int _maxJoinReplaceOneByteStringLength = 500; |
| |
| factory _StringBase._uninstantiable() { |
| throw new UnsupportedError("_StringBase can't be instaniated"); |
| } |
| |
| int get hashCode native "String_getHashCode"; |
| |
| bool get _isOneByte { |
| // Alternatively return false and override it on one-byte string classes. |
| int id = ClassID.getID(this); |
|