Chromium Code Reviews| 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 |
| 11 /// time, instead of them being emitted as part of the Object class. | 11 /// time, instead of them being emitted as part of the Object class. |
| 12 bool get generateTrivialNsmHandlers => true; | 12 bool get generateTrivialNsmHandlers => true; |
| 13 | 13 |
| 14 // If we need fewer than this many noSuchMethod handlers we can save space by | 14 // If we need fewer than this many noSuchMethod handlers we can save space by |
| 15 // just emitting them in JS, rather than emitting the JS needed to generate | 15 // just emitting them in JS, rather than emitting the JS needed to generate |
| 16 // them at run time. | 16 // them at run time. |
| 17 static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10; | 17 static const VERY_FEW_NO_SUCH_METHOD_HANDLERS = 10; |
| 18 | 18 |
| 19 static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4; | 19 static const MAX_MINIFIED_LENGTH_FOR_DIFF_ENCODING = 4; |
| 20 | 20 |
| 21 void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) { | 21 void emitNoSuchMethodHandlers(AddPropertyFunction addProperty) { |
| 22 // Do not generate no such method handlers if there is no class. | |
| 23 if (compiler.codegenWorld.directlyInstantiatedClasses.isEmpty) return; | |
| 24 | 22 |
| 25 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | 23 void computeSelectorsForNsmHandlers(Map<String, Selector> jsNames) { |
| 26 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
| 27 | 24 |
| 28 // Keep track of the JavaScript names we've already added so we | 25 // Do not generate no such method handlers if there is no class. |
| 29 // do not introduce duplicates (bad for code size). | 26 if (compiler.codegenWorld.directlyInstantiatedClasses.isEmpty) return; |
| 30 Map<String, Selector> addedJsNames = new Map<String, Selector>(); | |
| 31 | 27 |
| 32 void addNoSuchMethodHandlers(String ignore, Set<Selector> selectors) { | 28 void addNoSuchMethodHandlers(String ignore, Set<Selector> selectors) { |
| 33 // Cache the object class and type. | 29 TypeMask objectSubclassTypeMask = |
| 34 ClassElement objectClass = compiler.objectClass; | 30 new TypeMask.subclass(compiler.objectClass, compiler.world); |
| 35 DartType objectType = objectClass.rawType; | |
| 36 | 31 |
| 37 for (Selector selector in selectors) { | 32 for (Selector selector in selectors) { |
| 38 TypeMask mask = selector.mask; | 33 TypeMask mask = selector.mask; |
| 39 if (mask == null) { | 34 if (mask == null) mask = objectSubclassTypeMask; |
| 40 mask = new TypeMask.subclass(compiler.objectClass, compiler.world); | |
| 41 } | |
| 42 | 35 |
| 43 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) continue; | 36 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) { |
| 44 String jsName = namer.invocationMirrorInternalName(selector); | 37 continue; |
| 45 addedJsNames[jsName] = selector; | 38 } |
| 46 String reflectionName = emitter.getReflectionName(selector, jsName); | 39 String jsName = namer.invocationMirrorInternalName(selector); |
| 47 if (reflectionName != null) { | 40 jsNames[jsName] = selector; |
| 48 emitter.mangledFieldNames[jsName] = reflectionName; | |
| 49 } | 41 } |
| 50 } | 42 } |
| 43 | |
| 44 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); | |
| 45 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); | |
| 46 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); | |
| 51 } | 47 } |
| 52 | 48 |
| 53 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); | 49 jsAst.Expression generateMethod(Selector selector) { |
| 54 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); | |
| 55 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); | |
| 56 | |
| 57 // Set flag used by generateMethod helper below. If we have very few | |
| 58 // handlers we use addProperty for them all, rather than try to generate | |
| 59 // them at runtime. | |
| 60 bool haveVeryFewNoSuchMemberHandlers = | |
| 61 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); | |
| 62 | |
| 63 jsAst.Expression generateMethod(String jsName, Selector selector) { | |
| 64 // Values match JSInvocationMirror in js-helper library. | 50 // Values match JSInvocationMirror in js-helper library. |
| 65 int type = selector.invocationMirrorKind; | 51 int type = selector.invocationMirrorKind; |
| 66 List<String> parameterNames = | 52 List<String> parameterNames = |
| 67 new List.generate(selector.argumentCount, (i) => '\$$i'); | 53 new List.generate(selector.argumentCount, (i) => '\$$i'); |
| 68 | 54 |
| 69 List<jsAst.Expression> argNames = | 55 List<jsAst.Expression> argNames = |
| 70 selector.getOrderedNamedArguments().map((String name) => | 56 selector.getOrderedNamedArguments().map((String name) => |
| 71 js.string(name)).toList(); | 57 js.string(name)).toList(); |
| 72 | 58 |
| 73 String methodName = selector.invocationMirrorMemberName; | 59 String methodName = selector.invocationMirrorMemberName; |
| 74 String internalName = namer.invocationMirrorInternalName(selector); | 60 String internalName = namer.invocationMirrorInternalName(selector); |
| 75 String reflectionName = emitter.getReflectionName(selector, internalName); | |
| 76 if (!haveVeryFewNoSuchMemberHandlers && | |
| 77 isTrivialNsmHandler(type, argNames, selector, internalName) && | |
| 78 reflectionName == null) { | |
| 79 trivialNsmHandlers.add(selector); | |
| 80 return null; | |
| 81 } | |
| 82 | 61 |
| 83 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); | 62 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); |
| 84 jsAst.Expression expression = | 63 jsAst.Expression expression = |
| 85 js('''this.#noSuchMethodName(this, | 64 js('''this.#noSuchMethodName(this, |
| 86 #createInvocationMirror(#methodName, | 65 #createInvocationMirror(#methodName, |
| 87 #internalName, | 66 #internalName, |
| 88 #type, | 67 #type, |
| 89 #arguments, | 68 #arguments, |
| 90 #namedArguments))''', | 69 #namedArguments))''', |
| 91 {'noSuchMethodName': noSuchMethodName, | 70 {'noSuchMethodName': namer.noSuchMethodName, |
| 92 'createInvocationMirror': | 71 'createInvocationMirror': |
| 93 backend.emitter.staticFunctionAccess( | 72 backend.emitter.staticFunctionAccess( |
| 94 backend.getCreateInvocationMirror()), | 73 backend.getCreateInvocationMirror()), |
| 95 'methodName': | 74 'methodName': |
| 96 js.string(compiler.enableMinification | 75 js.string(compiler.enableMinification |
| 97 ? internalName : methodName), | 76 ? internalName : methodName), |
| 98 'internalName': js.string(internalName), | 77 'internalName': js.string(internalName), |
| 99 'type': js.number(type), | 78 'type': js.number(type), |
| 100 'arguments': | 79 'arguments': |
| 101 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), | 80 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), |
| 102 'namedArguments': new jsAst.ArrayInitializer(argNames)}); | 81 'namedArguments': new jsAst.ArrayInitializer(argNames)}); |
| 103 | 82 |
| 104 if (backend.isInterceptedName(selector.name)) { | 83 if (backend.isInterceptedName(selector.name)) { |
| 105 return js(r'function($receiver, #) { return # }', | 84 return js(r'function($receiver, #) { return # }', |
| 106 [parameterNames, expression]); | 85 [parameterNames, expression]); |
| 107 } else { | 86 } else { |
| 108 return js(r'function(#) { return # }', [parameterNames, expression]); | 87 return js(r'function(#) { return # }', [parameterNames, expression]); |
| 109 } | 88 } |
| 110 } | 89 } |
| 111 | 90 |
| 91 // Keep track of the JavaScript names we've already added so we | |
| 92 // do not introduce duplicates (bad for code size). | |
| 93 Map<String, Selector> addedJsNames = new Map<String, Selector>(); | |
| 94 computeSelectorsForNsmHandlers(addedJsNames); | |
|
floitsch
2015/02/03 10:56:02
Let the computeSelectors return the map.
zarah
2015/02/03 11:06:50
Done.
| |
| 95 | |
| 96 // Set flag used by generateMethod helper below. If we have very few | |
| 97 // handlers we use addProperty for them all, rather than try to generate | |
| 98 // them at runtime. | |
| 99 bool haveVeryFewNoSuchMemberHandlers = | |
| 100 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); | |
| 112 for (String jsName in addedJsNames.keys.toList()..sort()) { | 101 for (String jsName in addedJsNames.keys.toList()..sort()) { |
| 113 Selector selector = addedJsNames[jsName]; | 102 Selector selector = addedJsNames[jsName]; |
| 114 jsAst.Expression method = generateMethod(jsName, selector); | 103 String reflectionName = emitter.getReflectionName(selector, jsName); |
| 104 | |
| 105 if (reflectionName != null) { | |
| 106 emitter.mangledFieldNames[jsName] = reflectionName; | |
| 107 } | |
| 108 | |
| 109 List<jsAst.Expression> argNames = | |
| 110 selector.getOrderedNamedArguments().map((String name) => | |
| 111 js.string(name)).toList(); | |
| 112 int type = selector.invocationMirrorKind; | |
| 113 if (!haveVeryFewNoSuchMemberHandlers && | |
| 114 isTrivialNsmHandler(type, argNames, selector, jsName) && | |
| 115 reflectionName == null) { | |
| 116 trivialNsmHandlers.add(selector); | |
| 117 } | |
| 118 | |
| 119 jsAst.Expression method = generateMethod(selector); | |
| 115 if (method != null) { | 120 if (method != null) { |
| 116 addProperty(jsName, method); | 121 addProperty(jsName, method); |
| 117 String reflectionName = emitter.getReflectionName(selector, jsName); | |
| 118 if (reflectionName != null) { | 122 if (reflectionName != null) { |
| 119 bool accessible = compiler.world.allFunctions.filter(selector).any( | 123 bool accessible = compiler.world.allFunctions.filter(selector).any( |
| 120 (Element e) => backend.isAccessibleByReflection(e)); | 124 (Element e) => backend.isAccessibleByReflection(e)); |
| 121 addProperty('+$reflectionName', js(accessible ? '2' : '0')); | 125 addProperty('+$reflectionName', js(accessible ? '2' : '0')); |
| 122 } | 126 } |
| 123 } | 127 } |
| 124 } | 128 } |
| 125 } | 129 } |
| 126 | 130 |
| 127 // Identify the noSuchMethod handlers that are so simple that we can | 131 // Identify the noSuchMethod handlers that are so simple that we can |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 274 diffEncoding.write(short); | 278 diffEncoding.write(short); |
| 275 } | 279 } |
| 276 nameCounter++; | 280 nameCounter++; |
| 277 } | 281 } |
| 278 | 282 |
| 279 // Startup code that loops over the method names and puts handlers on the | 283 // Startup code that loops over the method names and puts handlers on the |
| 280 // Object class to catch noSuchMethod invocations. | 284 // Object class to catch noSuchMethod invocations. |
| 281 ClassElement objectClass = compiler.objectClass; | 285 ClassElement objectClass = compiler.objectClass; |
| 282 jsAst.Expression createInvocationMirror = backend.emitter | 286 jsAst.Expression createInvocationMirror = backend.emitter |
| 283 .staticFunctionAccess(backend.getCreateInvocationMirror()); | 287 .staticFunctionAccess(backend.getCreateInvocationMirror()); |
| 284 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | 288 String noSuchMethodName = namer.noSuchMethodName; |
|
floitsch
2015/02/03 10:56:02
might as well inline it to its use site.
zarah
2015/02/03 11:06:50
Done.
| |
| 285 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
| 286 var type = 0; | 289 var type = 0; |
| 287 if (useDiffEncoding) { | 290 if (useDiffEncoding) { |
| 288 statements.add(js.statement('''{ | 291 statements.add(js.statement('''{ |
| 289 var objectClassObject = processedClasses.collected[#objectClass], | 292 var objectClassObject = processedClasses.collected[#objectClass], |
| 290 shortNames = #diffEncoding.split(","), | 293 shortNames = #diffEncoding.split(","), |
| 291 nameNumber = 0, | 294 nameNumber = 0, |
| 292 diffEncodedString = shortNames[0], | 295 diffEncodedString = shortNames[0], |
| 293 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. | 296 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. |
| 294 // If we are loading a deferred library the object class will not be i n | 297 // If we are loading a deferred library the object class will not be i n |
| 295 // the collectedClasses so objectClassObject is undefined, and we skip | 298 // the collectedClasses so objectClassObject is undefined, and we skip |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 }''', { | 390 }''', { |
| 388 'sliceOffsetParams': sliceOffsetParams, | 391 'sliceOffsetParams': sliceOffsetParams, |
| 389 'noSuchMethodName': noSuchMethodName, | 392 'noSuchMethodName': noSuchMethodName, |
| 390 'createInvocationMirror': createInvocationMirror, | 393 'createInvocationMirror': createInvocationMirror, |
| 391 'names': minify ? 'shortNames' : 'longNames', | 394 'names': minify ? 'shortNames' : 'longNames', |
| 392 'sliceOffsetArguments': sliceOffsetArguments})); | 395 'sliceOffsetArguments': sliceOffsetArguments})); |
| 393 | 396 |
| 394 return statements; | 397 return statements; |
| 395 } | 398 } |
| 396 } | 399 } |
| OLD | NEW |