| // Copyright (c) 2024, 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. | |
| /// Values for the 'array flags' property that is attached to native JSArray and | |
| /// typed data objects to encode restrictions. | |
| /// | |
| /// We encode the restrictions as two bits - one for potentially growable | |
| /// objects that are restricted to be fixed length, and a second for objects | |
| /// with potentially modifiable contents that are restricted to have | |
| /// unmodifiable contents. We only use three combinations (00 = growable, 01 = | |
| /// fixed-length, 11 = unmodifiable), since the core List class does not have a | |
| /// variant that is an 'append-only' list (would be 10). | |
| /// | |
| /// We use the same scheme for typed data so that the same check works for | |
| /// modifiability checks for `a[i] = v` when `a` is a JSarray or Uint8Array. | |
| /// | |
| /// `const` JSArray values are tagged with an additional bit for future use in | |
| /// avoiding copying. We could also use the `const` bit for unmodifiable typed | |
| /// data for which there in no modifable view of the underlying buffer. | |
| /// | |
| /// Given the absense of append-only lists, we could have used a more | |
| /// numerically compact scheme (0 = growable, 1 = fixed-length, 2 = | |
| /// unmodifiable, 3 = const). This compact scheme requires comparison | |
| /// | |
| /// if (x.flags > 0) | |
| /// | |
| /// rather than mask-testing | |
| /// | |
| /// if (x.flags & 1) | |
| /// | |
| /// The unrestricted flags (0) are usually encoded as a missing property, i.e., | |
| /// `undefined`, which is converted to NaN for '>', but converted to the more | |
| /// comfortable '0' for '&'. We hope that there is less to go potentially go | |
| /// wrong with JavaScript engine slow paths with the bitmask. | |
| class ArrayFlags { | |
| /// Value of array flags that marks a JSArray as being fixed-length. This is | |
| /// not used on typed data since that is always fixed-length. | |
| static const int fixedLength = fixedLengthCheck; | |
| /// Value of array flags that marks a JSArray or typed-data object as | |
| /// unmodifiable. Includes the 'fixed-length' bit, since all unmodifiable | |
| /// lists are also fixed length. | |
| static const int unmodifiable = fixedLengthCheck | unmodifiableCheck; | |
| /// Value of array flags that marks a JSArray as a constant (transitively | |
| /// unmodifiable). | |
| static const int constant = | |
| fixedLengthCheck | unmodifiableCheck | constantCheck; | |
| /// Default value of array flags when there is no flags property. This value | |
| /// is not stored on the flags property. | |
| static const int none = 0; | |
| /// Bit to check for fixed-length JSArray. | |
| static const int fixedLengthCheck = 1; | |
| /// Bit to check for unmodifiable JSArray and typed data. | |
| static const int unmodifiableCheck = 2; | |
| /// Bit to check for constant JSArray. | |
| static const int constantCheck = 4; | |
| /// A List of operation names for HArrayFlagsCheck, encoded in a string of | |
| /// names separated by semicolons. | |
| /// | |
| /// The optimizer may replace the string with an index into this | |
| /// table. Generally this makes the generated code smaller. A name does not | |
| /// need to be in this table. It makes sense for this table to include only | |
| /// names that occur in more than one HArrayFlagsCheck, either as written in | |
| /// js_runtime, or occuring multiple times via inlining. | |
| static const operationNames = // | |
| '[]=' // This is the most common operation so it comes first. | |
| ';add' | |
| ';removeWhere' | |
| ';retainWhere' | |
| ';removeRange' | |
| ';setRange' | |
| ';setInt8' | |
| ';setInt16' | |
| ';setInt32' | |
| ';setUint8' | |
| ';setUint16' | |
| ';setUint32' | |
| ';setFloat32' | |
| ';setFloat64' // | |
| ; | |
| /// A list of 'verbs' for HArrayFlagsCheck, encoded as a string of phrases | |
| /// separated by semicolons. The 'verb' fills a span of the error message, for | |
| /// example, "remove from" in the message | |
| /// | |
| /// Cannot remove from an unmodifiable list. | |
| /// ^^^^^^^^^^^ | |
| /// | |
| /// The optimizer may replace the string with an index into this | |
| /// table. Generally this makes the program smaller. A 'verb' does not need to | |
| /// be in this table. It makes sense to only have verbs that are used by | |
| /// several calls to HArrayFlagsCheck, either as written in js_runtime, or | |
| /// occuring multiple times via inlining. | |
| static const verbs = // | |
| 'modify' // This is the verb for '[]=' so it comes first. | |
| ';remove from' | |
| ';add to' // | |
| ; | |
| /// A view of [operationNames] as a map from the name to the index of the name | |
| /// in the list. | |
| static final Map<String, int> operationNameToIndex = _invert(operationNames); | |
| /// A view of [verbs] as a map from the verb to the index of the verb in the | |
| /// list. | |
| static final Map<String, int> verbToIndex = _invert(verbs); | |
| static Map<String, int> _invert(String joinedStrings) { | |
| return { | |
| for (final entry in joinedStrings.split(';').asMap().entries) | |
| entry.value: entry.key, | |
| }; | |
| } | |
| } |