| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 class NsmEmitter extends CodeEmitterHelper { | 7 class NsmEmitter extends CodeEmitterHelper { |
| 8 final List<Selector> trivialNsmHandlers = <Selector>[]; | 8 final List<Selector> trivialNsmHandlers = <Selector>[]; |
| 9 | 9 |
| 10 /// If this is true then we can generate the noSuchMethod handlers at startup | 10 /// If this is true then we can generate the noSuchMethod handlers at startup |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 * decoding the base 88 numbers would overflow JavaScript's puny integers. | 115 * decoding the base 88 numbers would overflow JavaScript's puny integers. |
| 116 * | 116 * |
| 117 * There are some selectors that have a special calling convention (because | 117 * There are some selectors that have a special calling convention (because |
| 118 * they are called with the receiver as the first argument). They need a | 118 * they are called with the receiver as the first argument). They need a |
| 119 * slightly different noSuchMethod handler, so we handle these first. | 119 * slightly different noSuchMethod handler, so we handle these first. |
| 120 */ | 120 */ |
| 121 List<jsAst.Statement> buildTrivialNsmHandlers() { | 121 List<jsAst.Statement> buildTrivialNsmHandlers() { |
| 122 List<jsAst.Statement> statements = <jsAst.Statement>[]; | 122 List<jsAst.Statement> statements = <jsAst.Statement>[]; |
| 123 if (trivialNsmHandlers.length == 0) return statements; | 123 if (trivialNsmHandlers.length == 0) return statements; |
| 124 | 124 |
| 125 // Find out how many selectors there are with the special calling | |
| 126 // convention. | |
| 127 bool hasSpecialCallingConvention(Selector selector) { | |
| 128 return backend.isInterceptedName(selector.name); | |
| 129 } | |
| 130 Iterable<Selector> specialSelectors = trivialNsmHandlers.where( | |
| 131 (Selector s) => backend.isInterceptedName(s.name)); | |
| 132 Iterable<Selector> ordinarySelectors = trivialNsmHandlers.where( | |
| 133 (Selector s) => !backend.isInterceptedName(s.name)); | |
| 134 | |
| 135 // Get the short names (JS names, perhaps minified). | |
| 136 Iterable<jsAst.Name> specialShorts = | |
| 137 specialSelectors.map(namer.invocationMirrorInternalName); | |
| 138 Iterable<jsAst.Name> ordinaryShorts = | |
| 139 ordinarySelectors.map(namer.invocationMirrorInternalName); | |
| 140 | |
| 141 bool minify = compiler.enableMinification; | 125 bool minify = compiler.enableMinification; |
| 142 bool useDiffEncoding = minify && trivialNsmHandlers.length > 30; | 126 bool useDiffEncoding = minify && trivialNsmHandlers.length > 30; |
| 143 | 127 |
| 144 jsAst.Expression diffEncoding = new _DiffEncodedListOfNames( | 128 // Find out how many selectors there are with the special calling |
| 145 [specialShorts, ordinaryShorts], | 129 // convention. |
| 146 useDiffEncoding); | 130 Iterable<Selector> interceptedSelectors = trivialNsmHandlers.where( |
| 131 (Selector s) => backend.isInterceptedName(s.name)); |
| 132 Iterable<Selector> ordinarySelectors = trivialNsmHandlers.where( |
| 133 (Selector s) => !backend.isInterceptedName(s.name)); |
| 147 | 134 |
| 135 // Get the short names (JS names, perhaps minified). |
| 136 Iterable<jsAst.Name> interceptedShorts = |
| 137 interceptedSelectors.map(namer.invocationMirrorInternalName); |
| 138 Iterable<jsAst.Name> ordinaryShorts = |
| 139 ordinarySelectors.map(namer.invocationMirrorInternalName); |
| 140 |
| 141 jsAst.Expression sortedShorts; |
| 142 Iterable<String> sortedLongs; |
| 143 if (useDiffEncoding) { |
| 144 assert(minify); |
| 145 sortedShorts = new _DiffEncodedListOfNames( |
| 146 [interceptedShorts, ordinaryShorts]); |
| 147 } else { |
| 148 Iterable<Selector> sorted = |
| 149 [interceptedSelectors, ordinarySelectors].expand((e) => (e)); |
| 150 sortedShorts = js.concatenateStrings( |
| 151 js.joinLiterals( |
| 152 sorted.map(namer.invocationMirrorInternalName), |
| 153 js.stringPart(",")), |
| 154 addQuotes: true); |
| 155 |
| 156 if (!minify) { |
| 157 sortedLongs = sorted.map((selector) => |
| 158 selector.invocationMirrorMemberName); |
| 159 } |
| 160 } |
| 148 // Startup code that loops over the method names and puts handlers on the | 161 // Startup code that loops over the method names and puts handlers on the |
| 149 // Object class to catch noSuchMethod invocations. | 162 // Object class to catch noSuchMethod invocations. |
| 150 ClassElement objectClass = compiler.objectClass; | 163 ClassElement objectClass = compiler.objectClass; |
| 151 jsAst.Expression createInvocationMirror = backend.emitter | 164 jsAst.Expression createInvocationMirror = backend.emitter |
| 152 .staticFunctionAccess(backend.getCreateInvocationMirror()); | 165 .staticFunctionAccess(backend.getCreateInvocationMirror()); |
| 153 if (useDiffEncoding) { | 166 if (useDiffEncoding) { |
| 154 statements.add(js.statement('''{ | 167 statements.add(js.statement('''{ |
| 155 var objectClassObject = processedClasses.collected[#objectClass], | 168 var objectClassObject = processedClasses.collected[#objectClass], |
| 156 nameSequences = #diffEncoding.split("."), | 169 nameSequences = #diffEncoding.split("."), |
| 157 shortNames = []; | 170 shortNames = []; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 185 codes.unshift(${$HASH} + remaining % 88); | 198 codes.unshift(${$HASH} + remaining % 88); |
| 186 } | 199 } |
| 187 shortNames.push( | 200 shortNames.push( |
| 188 String.fromCharCode.apply(String, codes)); | 201 String.fromCharCode.apply(String, codes)); |
| 189 } | 202 } |
| 190 if (sequence.length > 1) { | 203 if (sequence.length > 1) { |
| 191 Array.prototype.push.apply(shortNames, sequence.shift()); | 204 Array.prototype.push.apply(shortNames, sequence.shift()); |
| 192 } | 205 } |
| 193 } | 206 } |
| 194 }''', {'objectClass': js.quoteName(namer.className(objectClass)), | 207 }''', {'objectClass': js.quoteName(namer.className(objectClass)), |
| 195 'diffEncoding': diffEncoding})); | 208 'diffEncoding': sortedShorts})); |
| 196 } else { | 209 } else { |
| 197 // No useDiffEncoding version. | 210 // No useDiffEncoding version. |
| 198 Iterable<String> longs = trivialNsmHandlers.map((selector) => | |
| 199 selector.invocationMirrorMemberName); | |
| 200 statements.add(js.statement( | 211 statements.add(js.statement( |
| 201 'var objectClassObject = processedClasses.collected[#objectClass],' | 212 'var objectClassObject = processedClasses.collected[#objectClass],' |
| 202 ' shortNames = #diffEncoding.split(",")', | 213 ' shortNames = #diffEncoding.split(",")', |
| 203 {'objectClass': js.quoteName(namer.className(objectClass)), | 214 {'objectClass': js.quoteName(namer.className(objectClass)), |
| 204 'diffEncoding': diffEncoding})); | 215 'diffEncoding': sortedShorts})); |
| 205 if (!minify) { | 216 if (!minify) { |
| 206 statements.add(js.statement('var longNames = #longs.split(",")', | 217 statements.add(js.statement('var longNames = #longs.split(",")', |
| 207 {'longs': js.string(longs.join(','))})); | 218 {'longs': js.string(sortedLongs.join(','))})); |
| 208 } | 219 } |
| 209 statements.add(js.statement( | 220 statements.add(js.statement( |
| 210 'if (objectClassObject instanceof Array)' | 221 'if (objectClassObject instanceof Array)' |
| 211 ' objectClassObject = objectClassObject[1];')); | 222 ' objectClassObject = objectClassObject[1];')); |
| 212 } | 223 } |
| 213 | 224 |
| 214 dynamic isIntercepted = // jsAst.Expression or bool. | 225 dynamic isIntercepted = // jsAst.Expression or bool. |
| 215 specialSelectors.isEmpty | 226 interceptedSelectors.isEmpty |
| 216 ? false | 227 ? false |
| 217 : ordinarySelectors.isEmpty | 228 : ordinarySelectors.isEmpty |
| 218 ? true | 229 ? true |
| 219 : js('j < #', js.number(specialSelectors.length)); | 230 : js('j < #', js.number(interceptedSelectors.length)); |
| 220 | 231 |
| 221 statements.add(js.statement(''' | 232 statements.add(js.statement(''' |
| 222 // If we are loading a deferred library the object class will not be in | 233 // If we are loading a deferred library the object class will not be in |
| 223 // the collectedClasses so objectClassObject is undefined, and we skip | 234 // the collectedClasses so objectClassObject is undefined, and we skip |
| 224 // setting up the names. | 235 // setting up the names. |
| 225 if (objectClassObject) { | 236 if (objectClassObject) { |
| 226 for (var j = 0; j < shortNames.length; j++) { | 237 for (var j = 0; j < shortNames.length; j++) { |
| 227 var type = 0; | 238 var type = 0; |
| 228 var shortName = shortNames[j]; | 239 var shortName = shortNames[j]; |
| 229 if (shortName[0] == "${namer.getterPrefix[0]}") type = 1; | 240 if (shortName[0] == "${namer.getterPrefix[0]}") type = 1; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 276 } | 287 } |
| 277 | 288 |
| 278 /// When pretty printed, this node computes a diff-encoded string for the list | 289 /// When pretty printed, this node computes a diff-encoded string for the list |
| 279 /// of given names. | 290 /// of given names. |
| 280 /// | 291 /// |
| 281 /// See [buildTrivialNsmHandlers]. | 292 /// See [buildTrivialNsmHandlers]. |
| 282 class _DiffEncodedListOfNames extends jsAst.DeferredString | 293 class _DiffEncodedListOfNames extends jsAst.DeferredString |
| 283 implements AstContainer { | 294 implements AstContainer { |
| 284 String _cachedValue; | 295 String _cachedValue; |
| 285 jsAst.ArrayInitializer ast; | 296 jsAst.ArrayInitializer ast; |
| 286 bool useDiffEncoding; | |
| 287 | 297 |
| 288 _DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names, | 298 _DiffEncodedListOfNames(Iterable<Iterable<jsAst.Name>> names) { |
| 289 this.useDiffEncoding) { | |
| 290 // Store the names in ArrayInitializer nodes to make them discoverable | 299 // Store the names in ArrayInitializer nodes to make them discoverable |
| 291 // by traversals of the ast. | 300 // by traversals of the ast. |
| 292 ast = new jsAst.ArrayInitializer( | 301 ast = new jsAst.ArrayInitializer( |
| 293 names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList())) | 302 names.map((Iterable i) => new jsAst.ArrayInitializer(i.toList())) |
| 294 .toList()); | 303 .toList()); |
| 295 } | 304 } |
| 296 | 305 |
| 297 void _computeDiffEncodingForList(Iterable<jsAst.Name> names, | 306 void _computeDiffEncodingForList(Iterable<jsAst.Name> names, |
| 298 StringBuffer diffEncoding) { | 307 StringBuffer diffEncoding) { |
| 299 // Treat string as a number in base 88 with digits in ASCII order from # to | 308 // Treat string as a number in base 88 with digits in ASCII order from # to |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 return a.compareTo(b); | 343 return a.compareTo(b); |
| 335 } | 344 } |
| 336 | 345 |
| 337 List<String> shorts = | 346 List<String> shorts = |
| 338 names.map((jsAst.Name name) => name.name) | 347 names.map((jsAst.Name name) => name.name) |
| 339 .toList() | 348 .toList() |
| 340 ..sort(compare); | 349 ..sort(compare); |
| 341 | 350 |
| 342 int previous = 0; | 351 int previous = 0; |
| 343 for (String short in shorts) { | 352 for (String short in shorts) { |
| 344 if (useDiffEncoding && | 353 if (short.length <= NsmEmitter.MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) { |
| 345 short.length <= NsmEmitter.MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING) { | |
| 346 int base63 = fromBase88(short); | 354 int base63 = fromBase88(short); |
| 347 int diff = base63 - previous; | 355 int diff = base63 - previous; |
| 348 previous = base63; | 356 previous = base63; |
| 349 String base26Diff = toBase26(diff); | 357 String base26Diff = toBase26(diff); |
| 350 diffEncoding.write(base26Diff); | 358 diffEncoding.write(base26Diff); |
| 351 } else { | 359 } else { |
| 352 if (useDiffEncoding || diffEncoding.length != 0) { | 360 if (diffEncoding.length != 0) { |
| 353 diffEncoding.write(','); | 361 diffEncoding.write(','); |
| 354 } | 362 } |
| 355 diffEncoding.write(short); | 363 diffEncoding.write(short); |
| 356 } | 364 } |
| 357 } | 365 } |
| 358 } | 366 } |
| 359 | 367 |
| 360 String _computeDiffEncoding() { | 368 String _computeDiffEncoding() { |
| 361 StringBuffer buffer = new StringBuffer(); | 369 StringBuffer buffer = new StringBuffer(); |
| 362 for (jsAst.ArrayInitializer list in ast.elements) { | 370 for (jsAst.ArrayInitializer list in ast.elements) { |
| 363 if (buffer.isNotEmpty) { | 371 if (buffer.isNotEmpty) { |
| 364 if (useDiffEncoding) { | 372 // Emit period that resets the diff base to zero when we switch to |
| 365 // Emit period that resets the diff base to zero when we switch to | 373 // normal calling convention (this avoids the need to code negative |
| 366 // normal calling convention (this avoids the need to code negative | 374 // diffs). |
| 367 // diffs). | 375 buffer.write("."); |
| 368 buffer.write("."); | |
| 369 } else { | |
| 370 // Write a separator for the next sequence. | |
| 371 buffer.write(","); | |
| 372 } | |
| 373 } | 376 } |
| 374 List<jsAst.Name> names = list.elements; | 377 List<jsAst.Name> names = list.elements; |
| 375 _computeDiffEncodingForList(names, buffer); | 378 _computeDiffEncodingForList(names, buffer); |
| 376 } | 379 } |
| 377 return '"${buffer.toString()}"'; | 380 return '"${buffer.toString()}"'; |
| 378 } | 381 } |
| 379 | 382 |
| 380 String get value { | 383 String get value { |
| 381 if (_cachedValue == null) { | 384 if (_cachedValue == null) { |
| 382 _cachedValue = _computeDiffEncoding(); | 385 _cachedValue = _computeDiffEncoding(); |
| 383 } | 386 } |
| 384 | 387 |
| 385 return _cachedValue; | 388 return _cachedValue; |
| 386 } | 389 } |
| 387 } | 390 } |
| OLD | NEW |