OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library dart._js_names; | |
6 | |
7 import 'dart:_js_embedded_names' show | |
8 JsGetName, | |
9 MANGLED_GLOBAL_NAMES, | |
10 MANGLED_NAMES; | |
11 | |
12 import 'dart:_foreign_helper' show | |
13 JS, | |
14 JS_EMBEDDED_GLOBAL, | |
15 JS_GET_NAME; | |
16 | |
17 import 'dart:_js_helper' show | |
18 JsCache, | |
19 NoInline; | |
20 | |
21 import 'dart:_interceptors' show JSArray; | |
22 | |
23 /// No-op method that is called to inform the compiler that unmangled named | |
24 /// must be preserved. | |
25 preserveNames() {} | |
26 | |
27 /// A map from mangled names to "reflective" names, that is, unmangled names | |
28 /// with some additional information, such as, number of required arguments. | |
29 /// This map is for mangled names used as instance members. | |
30 final _LazyMangledNamesMap mangledNames = new _LazyMangledInstanceNamesMap( | |
31 JS_EMBEDDED_GLOBAL('=Object', MANGLED_NAMES)); | |
32 | |
33 /// A map from "reflective" names to mangled names (the reverse of | |
34 /// [mangledNames]). | |
35 final _LazyReflectiveNamesMap reflectiveNames = | |
36 new _LazyReflectiveNamesMap(JS_EMBEDDED_GLOBAL('=Object', MANGLED_NAMES), | |
37 true); | |
38 | |
39 /// A map from mangled names to "reflective" names (see [mangledNames]). This | |
40 /// map is for globals, that is, static and top-level members. | |
41 final _LazyMangledNamesMap mangledGlobalNames = new _LazyMangledNamesMap( | |
42 JS_EMBEDDED_GLOBAL('=Object', MANGLED_GLOBAL_NAMES)); | |
43 | |
44 /// A map from "reflective" names to mangled names (the reverse of | |
45 /// [mangledGlobalNames]). | |
46 final _LazyReflectiveNamesMap reflectiveGlobalNames = | |
47 new _LazyReflectiveNamesMap( | |
48 JS_EMBEDDED_GLOBAL('=Object', MANGLED_GLOBAL_NAMES), false); | |
49 | |
50 /// Implements a mapping from mangled names to their reflective counterparts. | |
51 /// The propertiy names of [_jsMangledNames] are the mangled names, and the | |
52 /// values are the "reflective" names. | |
53 class _LazyMangledNamesMap { | |
54 /// [_jsMangledNames] is a JavaScript object literal. | |
55 var _jsMangledNames; | |
56 | |
57 _LazyMangledNamesMap(this._jsMangledNames); | |
58 | |
59 String operator[](String key) { | |
60 var result = JS('var', '#[#]', _jsMangledNames, key); | |
61 // Filter out all non-string values to protect against polution from | |
62 // anciliary fields in [_jsMangledNames]. | |
63 bool filter = | |
64 JS('bool', 'typeof # !== "string"', result); | |
65 // To ensure that the inferrer sees that result is a String, we explicitly | |
66 // give it a better type here. | |
67 return filter ? null : JS('String', '#', result); | |
68 } | |
69 } | |
70 | |
71 /// Extends [_LazyMangledNamesMap] with additional support for adding mappings | |
72 /// from mangled setter names to their reflective counterpart by rewriting a | |
73 /// corresponding entry for a getter name, if it exists. | |
74 class _LazyMangledInstanceNamesMap extends _LazyMangledNamesMap { | |
75 _LazyMangledInstanceNamesMap(_jsMangledNames) : super(_jsMangledNames); | |
76 | |
77 String operator[](String key) { | |
78 String result = super[key]; | |
79 String setterPrefix = JS_GET_NAME(JsGetName.SETTER_PREFIX); | |
80 if (result == null && key.startsWith(setterPrefix)) { | |
81 String getterPrefix = JS_GET_NAME(JsGetName.GETTER_PREFIX); | |
82 int setterPrefixLength = setterPrefix.length; | |
83 | |
84 // Generate the setter name from the getter name. | |
85 key = '$getterPrefix${key.substring(setterPrefixLength)}'; | |
86 result = super[key]; | |
87 return (result != null) ? "${result}=" : null; | |
88 } | |
89 return result; | |
90 } | |
91 } | |
92 | |
93 /// Implements the inverse of [_LazyMangledNamesMap]. As it would be too | |
94 /// expensive to seach the mangled names map for a value that corresponds to | |
95 /// the lookup key on each invocation, we compute the full mapping in demand | |
96 /// and cache it. The cache is invalidated when the underlying [_jsMangledNames] | |
97 /// object changes its length. This condition is sufficient as the name mapping | |
98 /// can only grow over time. | |
99 /// When [_isInstance] is true, we also apply the inverse of the setter/getter | |
100 /// name conversion implemented by [_LazyMangledInstanceNamesMap]. | |
101 class _LazyReflectiveNamesMap { | |
102 /// [_jsMangledNames] is a JavaScript object literal. | |
103 final _jsMangledNames; | |
104 final bool _isInstance; | |
105 int _cacheLength = 0; | |
106 Map<String, String> _cache; | |
107 | |
108 _LazyReflectiveNamesMap(this._jsMangledNames, this._isInstance); | |
109 | |
110 Map<String, String> _updateReflectiveNames() { | |
111 preserveNames(); | |
112 Map<String, String> result = <String, String>{}; | |
113 List keys = JS('List', 'Object.keys(#)', _jsMangledNames); | |
114 for (String key in keys) { | |
115 var reflectiveName = JS('var', '#[#]', _jsMangledNames, key); | |
116 // Filter out all non-string values to protect against polution from | |
117 // anciliary fields in [_jsMangledNames]. | |
118 bool filter = JS('bool', 'typeof # !== "string"', reflectiveName); | |
119 if (filter) continue; | |
120 result[reflectiveName] = JS('String', '#', key); | |
121 | |
122 String getterPrefix = JS_GET_NAME(JsGetName.GETTER_PREFIX); | |
123 if (_isInstance && key.startsWith(getterPrefix)) { | |
124 int getterPrefixLength = getterPrefix.length; | |
125 String setterPrefix = JS_GET_NAME(JsGetName.SETTER_PREFIX); | |
126 result['$reflectiveName='] = | |
127 '$setterPrefix${key.substring(getterPrefixLength)}'; | |
128 } | |
129 } | |
130 return result; | |
131 } | |
132 | |
133 int get _jsMangledNamesLength => JS('int', 'Object.keys(#).length', | |
134 _jsMangledNames); | |
135 | |
136 String operator[](String key) { | |
137 if (_cache == null || _jsMangledNamesLength != _cacheLength) { | |
138 _cache = _updateReflectiveNames(); | |
139 _cacheLength = _jsMangledNamesLength; | |
140 } | |
141 return _cache[key]; | |
142 } | |
143 } | |
144 | |
145 @NoInline() | |
146 List extractKeys(victim) { | |
147 var result = JS('', '# ? Object.keys(#) : []', victim, victim); | |
148 return new JSArray.markFixed(result); | |
149 } | |
150 | |
151 /** | |
152 * Returns the (global) unmangled version of [name]. | |
153 * | |
154 * Normally, you should use [mangledGlobalNames] directly, but this method | |
155 * doesn't tell the compiler to preserve names. So this method only returns a | |
156 * non-null value if some other component has made the compiler preserve names. | |
157 * | |
158 * This is used, for example, to return unmangled names from TypeImpl.toString | |
159 * *if* names are being preserved for other reasons (use of dart:mirrors, for | |
160 * example). | |
161 */ | |
162 String unmangleGlobalNameIfPreservedAnyways(String name) { | |
163 var names = JS_EMBEDDED_GLOBAL('=Object', MANGLED_GLOBAL_NAMES); | |
164 return JsCache.fetch(names, name); | |
165 } | |
166 | |
167 String unmangleAllIdentifiersIfPreservedAnyways(String str) { | |
168 return JS("String", | |
169 r"(#).replace(/[^<,> ]+/g," | |
170 r"function(m) { return #[m] || m; })", | |
171 str, | |
172 JS_EMBEDDED_GLOBAL('', MANGLED_GLOBAL_NAMES)); | |
173 } | |
OLD | NEW |