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 // Cache the object class and type. |
| 34 ClassElement objectClass = compiler.objectClass; | 30 ClassElement objectClass = compiler.objectClass; |
|
herhut
2015/02/03 09:39:53
How about caching the TypeMask here. Then it can b
zarah
2015/02/03 10:07:19
Done.
| |
| 35 DartType objectType = objectClass.rawType; | 31 DartType objectType = objectClass.rawType; |
|
herhut
2015/02/03 09:39:53
Is [objectType] used?
zarah
2015/02/03 10:07:19
Nope :-)
| |
| 36 | 32 |
| 37 for (Selector selector in selectors) { | 33 for (Selector selector in selectors) { |
| 38 TypeMask mask = selector.mask; | 34 TypeMask mask = selector.mask; |
| 39 if (mask == null) { | 35 if (mask == null) { |
| 40 mask = new TypeMask.subclass(compiler.objectClass, compiler.world); | 36 mask = new TypeMask.subclass(compiler.objectClass, |
| 41 } | 37 compiler.world); |
| 38 } | |
| 42 | 39 |
| 43 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) continue; | 40 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) { |
| 44 String jsName = namer.invocationMirrorInternalName(selector); | 41 continue; |
| 45 addedJsNames[jsName] = selector; | 42 } |
| 46 String reflectionName = emitter.getReflectionName(selector, jsName); | 43 String jsName = namer.invocationMirrorInternalName(selector); |
| 47 if (reflectionName != null) { | 44 jsNames[jsName] = selector; |
| 48 emitter.mangledFieldNames[jsName] = reflectionName; | |
| 49 } | 45 } |
| 50 } | 46 } |
| 47 | |
| 48 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); | |
| 49 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); | |
| 50 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); | |
| 51 } | 51 } |
| 52 | 52 |
| 53 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); | 53 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. | 54 // Values match JSInvocationMirror in js-helper library. |
| 65 int type = selector.invocationMirrorKind; | 55 int type = selector.invocationMirrorKind; |
| 66 List<String> parameterNames = | 56 List<String> parameterNames = |
| 67 new List.generate(selector.argumentCount, (i) => '\$$i'); | 57 new List.generate(selector.argumentCount, (i) => '\$$i'); |
| 68 | 58 |
| 69 List<jsAst.Expression> argNames = | 59 List<jsAst.Expression> argNames = |
| 70 selector.getOrderedNamedArguments().map((String name) => | 60 selector.getOrderedNamedArguments().map((String name) => |
| 71 js.string(name)).toList(); | 61 js.string(name)).toList(); |
| 72 | 62 |
| 73 String methodName = selector.invocationMirrorMemberName; | 63 String methodName = selector.invocationMirrorMemberName; |
| 74 String internalName = namer.invocationMirrorInternalName(selector); | 64 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 | 65 |
| 83 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); | 66 assert(backend.isInterceptedName(Compiler.NO_SUCH_METHOD)); |
| 84 jsAst.Expression expression = | 67 jsAst.Expression expression = |
| 85 js('''this.#noSuchMethodName(this, | 68 js('''this.#noSuchMethodName(this, |
| 86 #createInvocationMirror(#methodName, | 69 #createInvocationMirror(#methodName, |
| 87 #internalName, | 70 #internalName, |
| 88 #type, | 71 #type, |
| 89 #arguments, | 72 #arguments, |
| 90 #namedArguments))''', | 73 #namedArguments))''', |
| 91 {'noSuchMethodName': noSuchMethodName, | 74 {'noSuchMethodName': namer.noSuchMethodName, |
| 92 'createInvocationMirror': | 75 'createInvocationMirror': |
| 93 backend.emitter.staticFunctionAccess( | 76 backend.emitter.staticFunctionAccess( |
| 94 backend.getCreateInvocationMirror()), | 77 backend.getCreateInvocationMirror()), |
| 95 'methodName': | 78 'methodName': |
| 96 js.string(compiler.enableMinification | 79 js.string(compiler.enableMinification |
| 97 ? internalName : methodName), | 80 ? internalName : methodName), |
| 98 'internalName': js.string(internalName), | 81 'internalName': js.string(internalName), |
| 99 'type': js.number(type), | 82 'type': js.number(type), |
| 100 'arguments': | 83 'arguments': |
| 101 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), | 84 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), |
| 102 'namedArguments': new jsAst.ArrayInitializer(argNames)}); | 85 'namedArguments': new jsAst.ArrayInitializer(argNames)}); |
| 103 | 86 |
| 104 if (backend.isInterceptedName(selector.name)) { | 87 if (backend.isInterceptedName(selector.name)) { |
| 105 return js(r'function($receiver, #) { return # }', | 88 return js(r'function($receiver, #) { return # }', |
| 106 [parameterNames, expression]); | 89 [parameterNames, expression]); |
| 107 } else { | 90 } else { |
| 108 return js(r'function(#) { return # }', [parameterNames, expression]); | 91 return js(r'function(#) { return # }', [parameterNames, expression]); |
| 109 } | 92 } |
| 110 } | 93 } |
| 111 | 94 |
| 95 // Keep track of the JavaScript names we've already added so we | |
| 96 // do not introduce duplicates (bad for code size). | |
| 97 Map<String, Selector> addedJsNames = new Map<String, Selector>(); | |
| 98 computeSelectorsForNsmHandlers(addedJsNames); | |
| 99 | |
| 100 // Set flag used by generateMethod helper below. If we have very few | |
| 101 // handlers we use addProperty for them all, rather than try to generate | |
| 102 // them at runtime. | |
| 103 bool haveVeryFewNoSuchMemberHandlers = | |
| 104 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); | |
| 112 for (String jsName in addedJsNames.keys.toList()..sort()) { | 105 for (String jsName in addedJsNames.keys.toList()..sort()) { |
| 113 Selector selector = addedJsNames[jsName]; | 106 Selector selector = addedJsNames[jsName]; |
| 107 String reflectionName = emitter.getReflectionName(selector, jsName); | |
| 108 | |
| 109 if (reflectionName != null) { | |
| 110 emitter.mangledFieldNames[jsName] = reflectionName; | |
| 111 } | |
| 112 | |
| 113 List<jsAst.Expression> argNames = | |
| 114 selector.getOrderedNamedArguments().map((String name) => | |
| 115 js.string(name)).toList(); | |
| 116 int type = selector.invocationMirrorKind; | |
| 117 if (!haveVeryFewNoSuchMemberHandlers && | |
| 118 isTrivialNsmHandler(type, argNames, selector, jsName) && | |
| 119 reflectionName == null) { | |
| 120 trivialNsmHandlers.add(selector); | |
| 121 } | |
| 122 | |
| 114 jsAst.Expression method = generateMethod(jsName, selector); | 123 jsAst.Expression method = generateMethod(jsName, selector); |
| 115 if (method != null) { | 124 if (method != null) { |
| 116 addProperty(jsName, method); | 125 addProperty(jsName, method); |
| 117 String reflectionName = emitter.getReflectionName(selector, jsName); | |
| 118 if (reflectionName != null) { | 126 if (reflectionName != null) { |
| 119 bool accessible = compiler.world.allFunctions.filter(selector).any( | 127 bool accessible = compiler.world.allFunctions.filter(selector).any( |
| 120 (Element e) => backend.isAccessibleByReflection(e)); | 128 (Element e) => backend.isAccessibleByReflection(e)); |
| 121 addProperty('+$reflectionName', js(accessible ? '2' : '0')); | 129 addProperty('+$reflectionName', js(accessible ? '2' : '0')); |
| 122 } | 130 } |
| 123 } | 131 } |
| 124 } | 132 } |
| 125 } | 133 } |
| 126 | 134 |
| 127 // Identify the noSuchMethod handlers that are so simple that we can | 135 // 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); | 282 diffEncoding.write(short); |
| 275 } | 283 } |
| 276 nameCounter++; | 284 nameCounter++; |
| 277 } | 285 } |
| 278 | 286 |
| 279 // Startup code that loops over the method names and puts handlers on the | 287 // Startup code that loops over the method names and puts handlers on the |
| 280 // Object class to catch noSuchMethod invocations. | 288 // Object class to catch noSuchMethod invocations. |
| 281 ClassElement objectClass = compiler.objectClass; | 289 ClassElement objectClass = compiler.objectClass; |
| 282 jsAst.Expression createInvocationMirror = backend.emitter | 290 jsAst.Expression createInvocationMirror = backend.emitter |
| 283 .staticFunctionAccess(backend.getCreateInvocationMirror()); | 291 .staticFunctionAccess(backend.getCreateInvocationMirror()); |
| 284 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | 292 String noSuchMethodName = namer.noSuchMethodName; |
| 285 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
| 286 var type = 0; | 293 var type = 0; |
| 287 if (useDiffEncoding) { | 294 if (useDiffEncoding) { |
| 288 statements.add(js.statement('''{ | 295 statements.add(js.statement('''{ |
| 289 var objectClassObject = processedClasses.collected[#objectClass], | 296 var objectClassObject = processedClasses.collected[#objectClass], |
| 290 shortNames = #diffEncoding.split(","), | 297 shortNames = #diffEncoding.split(","), |
| 291 nameNumber = 0, | 298 nameNumber = 0, |
| 292 diffEncodedString = shortNames[0], | 299 diffEncodedString = shortNames[0], |
| 293 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. | 300 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 | 301 // 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 | 302 // the collectedClasses so objectClassObject is undefined, and we skip |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 }''', { | 394 }''', { |
| 388 'sliceOffsetParams': sliceOffsetParams, | 395 'sliceOffsetParams': sliceOffsetParams, |
| 389 'noSuchMethodName': noSuchMethodName, | 396 'noSuchMethodName': noSuchMethodName, |
| 390 'createInvocationMirror': createInvocationMirror, | 397 'createInvocationMirror': createInvocationMirror, |
| 391 'names': minify ? 'shortNames' : 'longNames', | 398 'names': minify ? 'shortNames' : 'longNames', |
| 392 'sliceOffsetArguments': sliceOffsetArguments})); | 399 'sliceOffsetArguments': sliceOffsetArguments})); |
| 393 | 400 |
| 394 return statements; | 401 return statements; |
| 395 } | 402 } |
| 396 } | 403 } |
| OLD | NEW |