| 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 Map<String, Selector> jsNames = <String, Selector>{}; |
| 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 Map<String, Selector> computeSelectorsForNsmHandlers() { |
| 29 // do not introduce duplicates (bad for code size). | 26 // Do not generate no such method handlers if there is no class. |
| 30 Map<String, Selector> addedJsNames = new Map<String, Selector>(); | 27 if (compiler.codegenWorld.directlyInstantiatedClasses.isEmpty) { |
| 28 return jsNames; |
| 29 } |
| 31 | 30 |
| 32 void addNoSuchMethodHandlers(String ignore, Set<Selector> selectors) { | 31 void addNoSuchMethodHandlers(String ignore, Set<Selector> selectors) { |
| 33 // Cache the object class and type. | 32 TypeMask objectSubclassTypeMask = |
| 34 ClassElement objectClass = compiler.objectClass; | 33 new TypeMask.subclass(compiler.objectClass, compiler.world); |
| 35 DartType objectType = objectClass.rawType; | |
| 36 | 34 |
| 37 for (Selector selector in selectors) { | 35 for (Selector selector in selectors) { |
| 38 TypeMask mask = selector.mask; | 36 TypeMask mask = selector.mask; |
| 39 if (mask == null) { | 37 if (mask == null) mask = objectSubclassTypeMask; |
| 40 mask = new TypeMask.subclass(compiler.objectClass, compiler.world); | |
| 41 } | |
| 42 | 38 |
| 43 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) continue; | 39 if (!mask.needsNoSuchMethodHandling(selector, compiler.world)) { |
| 44 String jsName = namer.invocationMirrorInternalName(selector); | 40 continue; |
| 45 addedJsNames[jsName] = selector; | 41 } |
| 46 String reflectionName = emitter.getReflectionName(selector, jsName); | 42 String jsName = namer.invocationMirrorInternalName(selector); |
| 47 if (reflectionName != null) { | 43 jsNames[jsName] = selector; |
| 48 emitter.mangledFieldNames[jsName] = reflectionName; | |
| 49 } | 44 } |
| 50 } | 45 } |
| 46 |
| 47 compiler.codegenWorld.invokedNames.forEach(addNoSuchMethodHandlers); |
| 48 compiler.codegenWorld.invokedGetters.forEach(addNoSuchMethodHandlers); |
| 49 compiler.codegenWorld.invokedSetters.forEach(addNoSuchMethodHandlers); |
| 50 return jsNames; |
| 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 = computeSelectorsForNsmHandlers(); |
| 98 |
| 99 // Set flag used by generateMethod helper below. If we have very few |
| 100 // handlers we use addProperty for them all, rather than try to generate |
| 101 // them at runtime. |
| 102 bool haveVeryFewNoSuchMemberHandlers = |
| 103 (addedJsNames.length < VERY_FEW_NO_SUCH_METHOD_HANDLERS); |
| 112 for (String jsName in addedJsNames.keys.toList()..sort()) { | 104 for (String jsName in addedJsNames.keys.toList()..sort()) { |
| 113 Selector selector = addedJsNames[jsName]; | 105 Selector selector = addedJsNames[jsName]; |
| 114 jsAst.Expression method = generateMethod(jsName, selector); | 106 String reflectionName = emitter.getReflectionName(selector, jsName); |
| 107 |
| 108 if (reflectionName != null) { |
| 109 emitter.mangledFieldNames[jsName] = reflectionName; |
| 110 } |
| 111 |
| 112 List<jsAst.Expression> argNames = |
| 113 selector.getOrderedNamedArguments().map((String name) => |
| 114 js.string(name)).toList(); |
| 115 int type = selector.invocationMirrorKind; |
| 116 if (!haveVeryFewNoSuchMemberHandlers && |
| 117 isTrivialNsmHandler(type, argNames, selector, jsName) && |
| 118 reflectionName == null) { |
| 119 trivialNsmHandlers.add(selector); |
| 120 } |
| 121 |
| 122 jsAst.Expression method = generateMethod(selector); |
| 115 if (method != null) { | 123 if (method != null) { |
| 116 addProperty(jsName, method); | 124 addProperty(jsName, method); |
| 117 String reflectionName = emitter.getReflectionName(selector, jsName); | |
| 118 if (reflectionName != null) { | 125 if (reflectionName != null) { |
| 119 bool accessible = compiler.world.allFunctions.filter(selector).any( | 126 bool accessible = compiler.world.allFunctions.filter(selector).any( |
| 120 (Element e) => backend.isAccessibleByReflection(e)); | 127 (Element e) => backend.isAccessibleByReflection(e)); |
| 121 addProperty('+$reflectionName', js(accessible ? '2' : '0')); | 128 addProperty('+$reflectionName', js(accessible ? '2' : '0')); |
| 122 } | 129 } |
| 123 } | 130 } |
| 124 } | 131 } |
| 125 } | 132 } |
| 126 | 133 |
| 127 // Identify the noSuchMethod handlers that are so simple that we can | 134 // 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); | 281 diffEncoding.write(short); |
| 275 } | 282 } |
| 276 nameCounter++; | 283 nameCounter++; |
| 277 } | 284 } |
| 278 | 285 |
| 279 // Startup code that loops over the method names and puts handlers on the | 286 // Startup code that loops over the method names and puts handlers on the |
| 280 // Object class to catch noSuchMethod invocations. | 287 // Object class to catch noSuchMethod invocations. |
| 281 ClassElement objectClass = compiler.objectClass; | 288 ClassElement objectClass = compiler.objectClass; |
| 282 jsAst.Expression createInvocationMirror = backend.emitter | 289 jsAst.Expression createInvocationMirror = backend.emitter |
| 283 .staticFunctionAccess(backend.getCreateInvocationMirror()); | 290 .staticFunctionAccess(backend.getCreateInvocationMirror()); |
| 284 String noSuchMethodName = namer.publicInstanceMethodNameByArity( | |
| 285 Compiler.NO_SUCH_METHOD, Compiler.NO_SUCH_METHOD_ARG_COUNT); | |
| 286 var type = 0; | 291 var type = 0; |
| 287 if (useDiffEncoding) { | 292 if (useDiffEncoding) { |
| 288 statements.add(js.statement('''{ | 293 statements.add(js.statement('''{ |
| 289 var objectClassObject = processedClasses.collected[#objectClass], | 294 var objectClassObject = processedClasses.collected[#objectClass], |
| 290 shortNames = #diffEncoding.split(","), | 295 shortNames = #diffEncoding.split(","), |
| 291 nameNumber = 0, | 296 nameNumber = 0, |
| 292 diffEncodedString = shortNames[0], | 297 diffEncodedString = shortNames[0], |
| 293 calculatedShortNames = [0, 1]; // 0, 1 are args for splice. | 298 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 | 299 // 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 | 300 // the collectedClasses so objectClassObject is undefined, and we skip |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 return this.#noSuchMethodName(this, | 384 return this.#noSuchMethodName(this, |
| 380 #createInvocationMirror(name, short, type, | 385 #createInvocationMirror(name, short, type, |
| 381 Array.prototype.slice.call(arguments, | 386 Array.prototype.slice.call(arguments, |
| 382 #sliceOffsetParams), | 387 #sliceOffsetParams), |
| 383 [])); | 388 [])); |
| 384 } | 389 } |
| 385 })(#names[j], short, type, #sliceOffsetArguments); | 390 })(#names[j], short, type, #sliceOffsetArguments); |
| 386 } | 391 } |
| 387 }''', { | 392 }''', { |
| 388 'sliceOffsetParams': sliceOffsetParams, | 393 'sliceOffsetParams': sliceOffsetParams, |
| 389 'noSuchMethodName': noSuchMethodName, | 394 'noSuchMethodName': namer.noSuchMethodName, |
| 390 'createInvocationMirror': createInvocationMirror, | 395 'createInvocationMirror': createInvocationMirror, |
| 391 'names': minify ? 'shortNames' : 'longNames', | 396 'names': minify ? 'shortNames' : 'longNames', |
| 392 'sliceOffsetArguments': sliceOffsetArguments})); | 397 'sliceOffsetArguments': sliceOffsetArguments})); |
| 393 | 398 |
| 394 return statements; | 399 return statements; |
| 395 } | 400 } |
| 396 } | 401 } |
| OLD | NEW |