| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 ParameterStubGenerator { | 7 class ParameterStubGenerator { |
| 8 static final Set<Selector> emptySelectorSet = new Set<Selector>(); | 8 static final Set<Selector> emptySelectorSet = new Set<Selector>(); |
| 9 | 9 |
| 10 final Namer namer; | 10 final Namer namer; |
| 11 final Compiler compiler; | 11 final Compiler compiler; |
| 12 final JavaScriptBackend backend; | 12 final JavaScriptBackend backend; |
| 13 | 13 |
| 14 ParameterStubGenerator(this.compiler, this.namer, this.backend); | 14 ParameterStubGenerator(this.compiler, this.namer, this.backend); |
| 15 | 15 |
| 16 Emitter get emitter => backend.emitter.emitter; | 16 Emitter get emitter => backend.emitter.emitter; |
| 17 CodeEmitterTask get emitterTask => backend.emitter; | 17 CodeEmitterTask get emitterTask => backend.emitter; |
| 18 DiagnosticReporter get reporter => compiler.reporter; | 18 DiagnosticReporter get reporter => compiler.reporter; |
| 19 | 19 |
| 20 bool needsSuperGetter(FunctionElement element) => | 20 bool needsSuperGetter(FunctionElement element) => |
| 21 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); | 21 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * Generates stubs to handle invocation of methods with optional | 24 * Generates stubs to handle invocation of methods with optional |
| 25 * arguments. | 25 * arguments. |
| 26 * | 26 * |
| 27 * A method like `foo([x])` may be invoked by the following | 27 * A method like `foo([x])` may be invoked by the following |
| 28 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the | 28 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the |
| 29 * given [selector] and returns the generated [ParameterStubMethod]. | 29 * given [selector] and returns the generated [ParameterStubMethod]. |
| 30 * | 30 * |
| 31 * Returns null if no stub is needed. | 31 * Returns null if no stub is needed. |
| 32 * | 32 * |
| 33 * Members may be invoked in two ways: directly, or through a closure. In the | 33 * Members may be invoked in two ways: directly, or through a closure. In the |
| 34 * latter case the caller invokes the closure's `call` method. This method | 34 * latter case the caller invokes the closure's `call` method. This method |
| 35 * accepts two selectors. The returned stub method has the corresponding | 35 * accepts two selectors. The returned stub method has the corresponding |
| 36 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if | 36 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if |
| 37 * the input selector is non-null (and the member needs a stub). | 37 * the input selector is non-null (and the member needs a stub). |
| 38 */ | 38 */ |
| 39 ParameterStubMethod generateParameterStub(FunctionElement member, | 39 ParameterStubMethod generateParameterStub( |
| 40 Selector selector, | 40 FunctionElement member, Selector selector, Selector callSelector) { |
| 41 Selector callSelector) { | |
| 42 CallStructure callStructure = selector.callStructure; | 41 CallStructure callStructure = selector.callStructure; |
| 43 FunctionSignature parameters = member.functionSignature; | 42 FunctionSignature parameters = member.functionSignature; |
| 44 int positionalArgumentCount = callStructure.positionalArgumentCount; | 43 int positionalArgumentCount = callStructure.positionalArgumentCount; |
| 45 if (positionalArgumentCount == parameters.parameterCount) { | 44 if (positionalArgumentCount == parameters.parameterCount) { |
| 46 assert(callStructure.isUnnamed); | 45 assert(callStructure.isUnnamed); |
| 47 return null; | 46 return null; |
| 48 } | 47 } |
| 49 if (parameters.optionalParametersAreNamed && | 48 if (parameters.optionalParametersAreNamed && |
| 50 callStructure.namedArgumentCount == parameters.optionalParameterCount) { | 49 callStructure.namedArgumentCount == parameters.optionalParameterCount) { |
| 51 // If the selector has the same number of named arguments as the element, | 50 // If the selector has the same number of named arguments as the element, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 62 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | 61 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
| 63 // Use '$receiver' to avoid clashes with other parameter names. Using | 62 // Use '$receiver' to avoid clashes with other parameter names. Using |
| 64 // '$receiver' works because namer.safeVariableName used for getting | 63 // '$receiver' works because namer.safeVariableName used for getting |
| 65 // parameter names never returns a name beginning with a single '$'. | 64 // parameter names never returns a name beginning with a single '$'. |
| 66 String receiverArgumentName = r'$receiver'; | 65 String receiverArgumentName = r'$receiver'; |
| 67 | 66 |
| 68 // The parameters that this stub takes. | 67 // The parameters that this stub takes. |
| 69 List<jsAst.Parameter> parametersBuffer = | 68 List<jsAst.Parameter> parametersBuffer = |
| 70 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 69 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
| 71 // The arguments that will be passed to the real method. | 70 // The arguments that will be passed to the real method. |
| 72 List<jsAst.Expression> argumentsBuffer = | 71 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( |
| 73 new List<jsAst.Expression>( | 72 parameters.parameterCount + extraArgumentCount); |
| 74 parameters.parameterCount + extraArgumentCount); | |
| 75 | 73 |
| 76 int count = 0; | 74 int count = 0; |
| 77 if (isInterceptedMethod) { | 75 if (isInterceptedMethod) { |
| 78 count++; | 76 count++; |
| 79 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 77 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
| 80 argumentsBuffer[0] = js('#', receiverArgumentName); | 78 argumentsBuffer[0] = js('#', receiverArgumentName); |
| 81 } | 79 } |
| 82 | 80 |
| 83 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 81 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
| 84 // Includes extra receiver argument when using interceptor convention | 82 // Includes extra receiver argument when using interceptor convention |
| (...skipping 25 matching lines...) Expand all Loading... |
| 110 // down to the native method. | 108 // down to the native method. |
| 111 indexOfLastOptionalArgumentInParameters = count; | 109 indexOfLastOptionalArgumentInParameters = count; |
| 112 } | 110 } |
| 113 argumentsBuffer[count] = emitter.constantReference(value); | 111 argumentsBuffer[count] = emitter.constantReference(value); |
| 114 } | 112 } |
| 115 } | 113 } |
| 116 } | 114 } |
| 117 count++; | 115 count++; |
| 118 }); | 116 }); |
| 119 | 117 |
| 120 var body; // List or jsAst.Statement. | 118 var body; // List or jsAst.Statement. |
| 121 if (backend.nativeData.hasFixedBackendName(member)) { | 119 if (backend.nativeData.hasFixedBackendName(member)) { |
| 122 body = emitterTask.nativeEmitter.generateParameterStubStatements( | 120 body = emitterTask.nativeEmitter.generateParameterStubStatements( |
| 123 member, isInterceptedMethod, namer.invocationName(selector), | 121 member, |
| 124 parametersBuffer, argumentsBuffer, | 122 isInterceptedMethod, |
| 123 namer.invocationName(selector), |
| 124 parametersBuffer, |
| 125 argumentsBuffer, |
| 125 indexOfLastOptionalArgumentInParameters); | 126 indexOfLastOptionalArgumentInParameters); |
| 126 } else if (member.isInstanceMember) { | 127 } else if (member.isInstanceMember) { |
| 127 if (needsSuperGetter(member)) { | 128 if (needsSuperGetter(member)) { |
| 128 ClassElement superClass = member.enclosingClass; | 129 ClassElement superClass = member.enclosingClass; |
| 129 jsAst.Name methodName = namer.instanceMethodName(member); | 130 jsAst.Name methodName = namer.instanceMethodName(member); |
| 130 // When redirecting, we must ensure that we don't end up in a subclass. | 131 // When redirecting, we must ensure that we don't end up in a subclass. |
| 131 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | 132 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. |
| 132 // Instead we need to call the statically resolved target. | 133 // Instead we need to call the statically resolved target. |
| 133 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | 134 // `<class>.prototype.bar$1.call(this, argument0, ...)`. |
| 134 body = js.statement( | 135 body = js.statement('return #.#.call(this, #);', [ |
| 135 'return #.#.call(this, #);', | 136 backend.emitter |
| 136 [backend.emitter.prototypeAccess(superClass, | 137 .prototypeAccess(superClass, hasBeenInstantiated: true), |
| 137 hasBeenInstantiated: true), | 138 methodName, |
| 138 methodName, | 139 argumentsBuffer |
| 139 argumentsBuffer]); | 140 ]); |
| 140 } else { | 141 } else { |
| 141 body = js.statement( | 142 body = js.statement('return this.#(#);', |
| 142 'return this.#(#);', | |
| 143 [namer.instanceMethodName(member), argumentsBuffer]); | 143 [namer.instanceMethodName(member), argumentsBuffer]); |
| 144 } | 144 } |
| 145 } else { | 145 } else { |
| 146 body = js.statement('return #(#)', | 146 body = js.statement('return #(#)', |
| 147 [emitter.staticFunctionAccess(member), argumentsBuffer]); | 147 [emitter.staticFunctionAccess(member), argumentsBuffer]); |
| 148 } | 148 } |
| 149 | 149 |
| 150 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); | 150 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); |
| 151 | 151 |
| 152 jsAst.Name name = member.isStatic ? null : namer.invocationName(selector); | 152 jsAst.Name name = member.isStatic ? null : namer.invocationName(selector); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 180 // (4) No stub generated, call is direct. | 180 // (4) No stub generated, call is direct. |
| 181 // (5) No stub generated, call is direct. | 181 // (5) No stub generated, call is direct. |
| 182 // | 182 // |
| 183 // We need to pay attention if this stub is for a function that has been | 183 // We need to pay attention if this stub is for a function that has been |
| 184 // invoked from a subclass. Then we cannot just redirect, since that | 184 // invoked from a subclass. Then we cannot just redirect, since that |
| 185 // would invoke the methods of the subclass. We have to compile to: | 185 // would invoke the methods of the subclass. We have to compile to: |
| 186 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) | 186 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) |
| 187 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); | 187 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); |
| 188 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); | 188 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); |
| 189 List<ParameterStubMethod> generateParameterStubs(MethodElement member, | 189 List<ParameterStubMethod> generateParameterStubs(MethodElement member, |
| 190 {bool canTearOff: true}) { | 190 {bool canTearOff: true}) { |
| 191 if (member.enclosingElement.isClosure) { | 191 if (member.enclosingElement.isClosure) { |
| 192 ClosureClassElement cls = member.enclosingElement; | 192 ClosureClassElement cls = member.enclosingElement; |
| 193 if (cls.supertype.element == backend.helpers.boundClosureClass) { | 193 if (cls.supertype.element == backend.helpers.boundClosureClass) { |
| 194 reporter.internalError(cls.methodElement, 'Bound closure1.'); | 194 reporter.internalError(cls.methodElement, 'Bound closure1.'); |
| 195 } | 195 } |
| 196 if (cls.methodElement.isInstanceMember) { | 196 if (cls.methodElement.isInstanceMember) { |
| 197 reporter.internalError(cls.methodElement, 'Bound closure2.'); | 197 reporter.internalError(cls.methodElement, 'Bound closure2.'); |
| 198 } | 198 } |
| 199 } | 199 } |
| 200 | 200 |
| 201 // The set of selectors that apply to `member`. For example, for | 201 // The set of selectors that apply to `member`. For example, for |
| 202 // a member `foo(x, [y])` the following selectors may apply: | 202 // a member `foo(x, [y])` the following selectors may apply: |
| 203 // `foo(x)`, and `foo(x, y)`. | 203 // `foo(x)`, and `foo(x, y)`. |
| 204 Map<Selector, SelectorConstraints> selectors; | 204 Map<Selector, SelectorConstraints> selectors; |
| 205 // The set of selectors that apply to `member` if it's name was `call`. | 205 // The set of selectors that apply to `member` if it's name was `call`. |
| 206 // This happens when a member is torn off. In that case calls to the | 206 // This happens when a member is torn off. In that case calls to the |
| 207 // function use the name `call`, and we must be able to handle every | 207 // function use the name `call`, and we must be able to handle every |
| 208 // `call` invocation that matches the signature. For example, for | 208 // `call` invocation that matches the signature. For example, for |
| 209 // a member `foo(x, [y])` the following selectors would be possible | 209 // a member `foo(x, [y])` the following selectors would be possible |
| 210 // call-selectors: `call(x)`, and `call(x, y)`. | 210 // call-selectors: `call(x)`, and `call(x, y)`. |
| 211 Map<Selector, SelectorConstraints> callSelectors; | 211 Map<Selector, SelectorConstraints> callSelectors; |
| 212 | 212 |
| 213 // Only instance members (not static methods) need stubs. | 213 // Only instance members (not static methods) need stubs. |
| 214 if (member.isInstanceMember) { | 214 if (member.isInstanceMember) { |
| 215 selectors = compiler.codegenWorld.invocationsByName(member.name); | 215 selectors = compiler.codegenWorld.invocationsByName(member.name); |
| 216 } | 216 } |
| 217 | 217 |
| 218 if (canTearOff) { | 218 if (canTearOff) { |
| 219 String call = namer.closureInvocationSelectorName; | 219 String call = namer.closureInvocationSelectorName; |
| 220 callSelectors = compiler.codegenWorld.invocationsByName(call); | 220 callSelectors = compiler.codegenWorld.invocationsByName(call); |
| 221 } | 221 } |
| 222 | 222 |
| 223 assert(emptySelectorSet.isEmpty); | 223 assert(emptySelectorSet.isEmpty); |
| 224 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; | 224 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; |
| 225 if (callSelectors == null) callSelectors = | 225 if (callSelectors == null) |
| 226 const <Selector, SelectorConstraints>{}; | 226 callSelectors = const <Selector, SelectorConstraints>{}; |
| 227 | 227 |
| 228 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; | 228 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; |
| 229 | 229 |
| 230 if (selectors.isEmpty && callSelectors.isEmpty) { | 230 if (selectors.isEmpty && callSelectors.isEmpty) { |
| 231 return stubs; | 231 return stubs; |
| 232 } | 232 } |
| 233 | 233 |
| 234 // For every call-selector the corresponding selector with the name of the | 234 // For every call-selector the corresponding selector with the name of the |
| 235 // member. | 235 // member. |
| 236 // | 236 // |
| 237 // For example, for the call-selector `call(x, y)` the renamed selector | 237 // For example, for the call-selector `call(x, y)` the renamed selector |
| 238 // for member `foo` would be `foo(x, y)`. | 238 // for member `foo` would be `foo(x, y)`. |
| 239 Set<Selector> renamedCallSelectors = | 239 Set<Selector> renamedCallSelectors = |
| 240 callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>(); | 240 callSelectors.isEmpty ? emptySelectorSet : new Set<Selector>(); |
| 241 | 241 |
| 242 Set<Selector> untypedSelectors = new Set<Selector>(); | 242 Set<Selector> untypedSelectors = new Set<Selector>(); |
| 243 | 243 |
| 244 // Start with the callSelectors since they imply the generation of the | 244 // Start with the callSelectors since they imply the generation of the |
| 245 // non-call version. | 245 // non-call version. |
| 246 for (Selector selector in callSelectors.keys) { | 246 for (Selector selector in callSelectors.keys) { |
| 247 Selector renamedSelector = new Selector.call( | 247 Selector renamedSelector = |
| 248 member.memberName, | 248 new Selector.call(member.memberName, selector.callStructure); |
| 249 selector.callStructure); | |
| 250 renamedCallSelectors.add(renamedSelector); | 249 renamedCallSelectors.add(renamedSelector); |
| 251 | 250 |
| 252 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue; | 251 if (!renamedSelector.appliesUnnamed(member, compiler.world)) continue; |
| 253 | 252 |
| 254 if (untypedSelectors.add(renamedSelector)) { | 253 if (untypedSelectors.add(renamedSelector)) { |
| 255 ParameterStubMethod stub = | 254 ParameterStubMethod stub = |
| 256 generateParameterStub(member, renamedSelector, selector); | 255 generateParameterStub(member, renamedSelector, selector); |
| 257 if (stub != null) { | 256 if (stub != null) { |
| 258 stubs.add(stub); | 257 stubs.add(stub); |
| 259 } | 258 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 275 generateParameterStub(member, selector, null); | 274 generateParameterStub(member, selector, null); |
| 276 if (stub != null) { | 275 if (stub != null) { |
| 277 stubs.add(stub); | 276 stubs.add(stub); |
| 278 } | 277 } |
| 279 } | 278 } |
| 280 } | 279 } |
| 281 | 280 |
| 282 return stubs; | 281 return stubs; |
| 283 } | 282 } |
| 284 } | 283 } |
| OLD | NEW |