| // Copyright (c) 2013, 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. |
| |
| // @dart = 2.6 |
| |
| library dart._js_names; |
| |
| import 'dart:_js_embedded_names' |
| show JsGetName, MANGLED_GLOBAL_NAMES, MANGLED_NAMES; |
| |
| import 'dart:_foreign_helper' show JS, JS_EMBEDDED_GLOBAL, JS_GET_NAME; |
| |
| import 'dart:_js_helper' show JsCache, NoInline; |
| |
| import 'dart:_interceptors' show JSArray; |
| |
| /// No-op method that is called to inform the compiler that unmangled named |
| /// must be preserved. |
| preserveNames() {} |
| |
| /// A map from mangled names to "reflective" names, that is, unmangled names |
| /// with some additional information, such as, number of required arguments. |
| /// This map is for mangled names used as instance members. |
| final _LazyMangledNamesMap mangledNames = new _LazyMangledInstanceNamesMap( |
| JS_EMBEDDED_GLOBAL('=Object', MANGLED_NAMES)); |
| |
| /// A map from "reflective" names to mangled names (the reverse of |
| /// [mangledNames]). |
| final _LazyReflectiveNamesMap reflectiveNames = new _LazyReflectiveNamesMap( |
| JS_EMBEDDED_GLOBAL('=Object', MANGLED_NAMES), true); |
| |
| /// A map from mangled names to "reflective" names (see [mangledNames]). This |
| /// map is for globals, that is, static and top-level members. |
| final _LazyMangledNamesMap mangledGlobalNames = new _LazyMangledNamesMap( |
| JS_EMBEDDED_GLOBAL('=Object', MANGLED_GLOBAL_NAMES)); |
| |
| /// A map from "reflective" names to mangled names (the reverse of |
| /// [mangledGlobalNames]). |
| final _LazyReflectiveNamesMap reflectiveGlobalNames = |
| new _LazyReflectiveNamesMap( |
| JS_EMBEDDED_GLOBAL('=Object', MANGLED_GLOBAL_NAMES), false); |
| |
| /// Implements a mapping from mangled names to their reflective counterparts. |
| /// The propertiy names of [_jsMangledNames] are the mangled names, and the |
| /// values are the "reflective" names. |
| class _LazyMangledNamesMap { |
| /// [_jsMangledNames] is a JavaScript object literal. |
| var _jsMangledNames; |
| |
| _LazyMangledNamesMap(this._jsMangledNames); |
| |
| String operator [](String key) { |
| var result = JS('var', '#[#]', _jsMangledNames, key); |
| // Filter out all non-string values to protect against polution from |
| // ancillary fields in [_jsMangledNames]. |
| bool filter = JS('bool', 'typeof # !== "string"', result); |
| // To ensure that the inferrer sees that result is a String, we explicitly |
| // give it a better type here. |
| return filter ? null : JS('String', '#', result); |
| } |
| } |
| |
| /// Extends [_LazyMangledNamesMap] with additional support for adding mappings |
| /// from mangled setter names to their reflective counterpart by rewriting a |
| /// corresponding entry for a getter name, if it exists. |
| class _LazyMangledInstanceNamesMap extends _LazyMangledNamesMap { |
| _LazyMangledInstanceNamesMap(_jsMangledNames) : super(_jsMangledNames); |
| |
| String operator [](String key) { |
| String result = super[key]; |
| String setterPrefix = JS_GET_NAME(JsGetName.SETTER_PREFIX); |
| if (result == null && key.startsWith(setterPrefix)) { |
| String getterPrefix = JS_GET_NAME(JsGetName.GETTER_PREFIX); |
| int setterPrefixLength = setterPrefix.length; |
| |
| // Generate the setter name from the getter name. |
| key = '$getterPrefix${key.substring(setterPrefixLength)}'; |
| result = super[key]; |
| return (result != null) ? "${result}=" : null; |
| } |
| return result; |
| } |
| } |
| |
| /// Implements the inverse of [_LazyMangledNamesMap]. As it would be too |
| /// expensive to search the mangled names map for a value that corresponds to |
| /// the lookup key on each invocation, we compute the full mapping in demand |
| /// and cache it. The cache is invalidated when the underlying [_jsMangledNames] |
| /// object changes its length. This condition is sufficient as the name mapping |
| /// can only grow over time. |
| /// When [_isInstance] is true, we also apply the inverse of the setter/getter |
| /// name conversion implemented by [_LazyMangledInstanceNamesMap]. |
| class _LazyReflectiveNamesMap { |
| /// [_jsMangledNames] is a JavaScript object literal. |
| final _jsMangledNames; |
| final bool _isInstance; |
| int _cacheLength = 0; |
| Map<String, String> _cache; |
| |
| _LazyReflectiveNamesMap(this._jsMangledNames, this._isInstance); |
| |
| Map<String, String> _updateReflectiveNames() { |
| preserveNames(); |
| Map<String, String> result = <String, String>{}; |
| List keys = JS('List', 'Object.keys(#)', _jsMangledNames); |
| for (String key in keys) { |
| var reflectiveName = JS('var', '#[#]', _jsMangledNames, key); |
| // Filter out all non-string values to protect against polution from |
| // ancillary fields in [_jsMangledNames]. |
| bool filter = JS('bool', 'typeof # !== "string"', reflectiveName); |
| if (filter) continue; |
| result[reflectiveName] = JS('String', '#', key); |
| |
| String getterPrefix = JS_GET_NAME(JsGetName.GETTER_PREFIX); |
| if (_isInstance && key.startsWith(getterPrefix)) { |
| int getterPrefixLength = getterPrefix.length; |
| String setterPrefix = JS_GET_NAME(JsGetName.SETTER_PREFIX); |
| result['$reflectiveName='] = |
| '$setterPrefix${key.substring(getterPrefixLength)}'; |
| } |
| } |
| return result; |
| } |
| |
| int get _jsMangledNamesLength => |
| JS('int', 'Object.keys(#).length', _jsMangledNames); |
| |
| String operator [](String key) { |
| if (_cache == null || _jsMangledNamesLength != _cacheLength) { |
| _cache = _updateReflectiveNames(); |
| _cacheLength = _jsMangledNamesLength; |
| } |
| return _cache[key]; |
| } |
| } |
| |
| @pragma('dart2js:noInline') |
| List extractKeys(victim) { |
| var result = JS('', '# ? Object.keys(#) : []', victim, victim); |
| return new JSArray.markFixed(result); |
| } |
| |
| /// Returns the (global) unmangled version of [name]. |
| /// |
| /// Normally, you should use [mangledGlobalNames] directly, but this method |
| /// doesn't tell the compiler to preserve names. So this method only returns a |
| /// non-null value if some other component has made the compiler preserve names. |
| /// |
| /// This is used, for example, to return unmangled names from TypeImpl.toString |
| /// *if* names are being preserved for other reasons (use of dart:mirrors, for |
| /// example). |
| String unmangleGlobalNameIfPreservedAnyways(String name) { |
| var names = JS_EMBEDDED_GLOBAL('', MANGLED_GLOBAL_NAMES); |
| return JS('String|Null', '#', JsCache.fetch(names, name)); |
| } |
| |
| String unmangleAllIdentifiersIfPreservedAnyways(String str) { |
| return JS( |
| 'String', |
| r''' |
| (function(str, names) { |
| return str.replace( |
| /[^<,> ]+/g, |
| function(m) { return names[m] || m; }); |
| })(#, #)''', |
| str, |
| JS_EMBEDDED_GLOBAL('', MANGLED_GLOBAL_NAMES)); |
| } |