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 library dart2js.js_emitter.parameter_stub_generator; | 5 library dart2js.js_emitter.parameter_stub_generator; |
6 | 6 |
7 import '../closure.dart' show ClosureClassElement; | 7 import '../closure.dart' show ClosureClassElement; |
8 import '../common.dart'; | 8 import '../common.dart'; |
9 import '../compiler.dart' show Compiler; | 9 import '../common_elements.dart'; |
10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
11 import '../elements/elements.dart' | 11 import '../elements/elements.dart' |
12 show | 12 show |
13 ClassElement, | 13 ClassElement, |
14 FunctionElement, | 14 FunctionElement, |
15 FunctionSignature, | 15 FunctionSignature, |
16 MethodElement, | 16 MethodElement, |
17 ParameterElement; | 17 ParameterElement; |
18 import '../js/js.dart' as jsAst; | 18 import '../js/js.dart' as jsAst; |
19 import '../js/js.dart' show js; | 19 import '../js/js.dart' show js; |
20 import '../js_backend/js_backend.dart' | 20 import '../js_backend/constant_handler_javascript.dart' |
21 show JavaScriptBackend, JavaScriptConstantCompiler, Namer; | 21 show JavaScriptConstantCompiler; |
| 22 import '../js_backend/namer.dart' show Namer; |
| 23 import '../js_backend/native_data.dart'; |
| 24 import '../js_backend/interceptor_data.dart'; |
22 import '../universe/call_structure.dart' show CallStructure; | 25 import '../universe/call_structure.dart' show CallStructure; |
23 import '../universe/selector.dart' show Selector; | 26 import '../universe/selector.dart' show Selector; |
24 import '../universe/world_builder.dart' show SelectorConstraints; | 27 import '../universe/world_builder.dart' |
| 28 show CodegenWorldBuilder, SelectorConstraints; |
25 import '../world.dart' show ClosedWorld; | 29 import '../world.dart' show ClosedWorld; |
26 | 30 |
27 import 'model.dart'; | 31 import 'model.dart'; |
28 | 32 |
29 import 'code_emitter_task.dart' show CodeEmitterTask, Emitter; | 33 import 'code_emitter_task.dart' show CodeEmitterTask, Emitter; |
30 | 34 |
31 class ParameterStubGenerator { | 35 class ParameterStubGenerator { |
32 static final Set<Selector> emptySelectorSet = new Set<Selector>(); | 36 static final Set<Selector> emptySelectorSet = new Set<Selector>(); |
33 | 37 |
34 final Namer namer; | 38 final CommonElements _commonElements; |
35 final Compiler compiler; | 39 final CodeEmitterTask _emitterTask; |
36 final JavaScriptBackend backend; | 40 final JavaScriptConstantCompiler _constants; |
37 final ClosedWorld closedWorld; | 41 final Namer _namer; |
| 42 final NativeData _nativeData; |
| 43 final InterceptorData _interceptorData; |
| 44 final CodegenWorldBuilder _codegenWorldBuilder; |
| 45 final ClosedWorld _closedWorld; |
38 | 46 |
39 ParameterStubGenerator( | 47 ParameterStubGenerator( |
40 this.compiler, this.namer, this.backend, this.closedWorld); | 48 this._commonElements, |
| 49 this._emitterTask, |
| 50 this._constants, |
| 51 this._namer, |
| 52 this._nativeData, |
| 53 this._interceptorData, |
| 54 this._codegenWorldBuilder, |
| 55 this._closedWorld); |
41 | 56 |
42 Emitter get emitter => backend.emitter.emitter; | 57 Emitter get _emitter => _emitterTask.emitter; |
43 CodeEmitterTask get emitterTask => backend.emitter; | |
44 DiagnosticReporter get reporter => compiler.reporter; | |
45 | 58 |
46 bool needsSuperGetter(FunctionElement element) => | 59 bool needsSuperGetter(FunctionElement element) => |
47 compiler.codegenWorldBuilder.methodsNeedingSuperGetter.contains(element); | 60 _codegenWorldBuilder.methodsNeedingSuperGetter.contains(element); |
48 | 61 |
49 /** | 62 /** |
50 * Generates stubs to handle invocation of methods with optional | 63 * Generates stubs to handle invocation of methods with optional |
51 * arguments. | 64 * arguments. |
52 * | 65 * |
53 * A method like `foo([x])` may be invoked by the following | 66 * A method like `foo([x])` may be invoked by the following |
54 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the | 67 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the |
55 * given [selector] and returns the generated [ParameterStubMethod]. | 68 * given [selector] and returns the generated [ParameterStubMethod]. |
56 * | 69 * |
57 * Returns null if no stub is needed. | 70 * Returns null if no stub is needed. |
(...skipping 13 matching lines...) Expand all Loading... |
71 assert(callStructure.isUnnamed); | 84 assert(callStructure.isUnnamed); |
72 return null; | 85 return null; |
73 } | 86 } |
74 if (parameters.optionalParametersAreNamed && | 87 if (parameters.optionalParametersAreNamed && |
75 callStructure.namedArgumentCount == parameters.optionalParameterCount) { | 88 callStructure.namedArgumentCount == parameters.optionalParameterCount) { |
76 // If the selector has the same number of named arguments as the element, | 89 // If the selector has the same number of named arguments as the element, |
77 // we don't need to add a stub. The call site will hit the method | 90 // we don't need to add a stub. The call site will hit the method |
78 // directly. | 91 // directly. |
79 return null; | 92 return null; |
80 } | 93 } |
81 JavaScriptConstantCompiler handler = backend.constants; | |
82 List<String> names = callStructure.getOrderedNamedArguments(); | 94 List<String> names = callStructure.getOrderedNamedArguments(); |
83 | 95 |
84 bool isInterceptedMethod = | 96 bool isInterceptedMethod = _interceptorData.isInterceptedMethod(member); |
85 backend.interceptorData.isInterceptedMethod(member); | |
86 | 97 |
87 // If the method is intercepted, we need to also pass the actual receiver. | 98 // If the method is intercepted, we need to also pass the actual receiver. |
88 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | 99 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
89 // Use '$receiver' to avoid clashes with other parameter names. Using | 100 // Use '$receiver' to avoid clashes with other parameter names. Using |
90 // '$receiver' works because namer.safeVariableName used for getting | 101 // '$receiver' works because namer.safeVariableName used for getting |
91 // parameter names never returns a name beginning with a single '$'. | 102 // parameter names never returns a name beginning with a single '$'. |
92 String receiverArgumentName = r'$receiver'; | 103 String receiverArgumentName = r'$receiver'; |
93 | 104 |
94 // The parameters that this stub takes. | 105 // The parameters that this stub takes. |
95 List<jsAst.Parameter> parametersBuffer = | 106 List<jsAst.Parameter> parametersBuffer = |
96 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 107 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
97 // The arguments that will be passed to the real method. | 108 // The arguments that will be passed to the real method. |
98 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( | 109 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( |
99 parameters.parameterCount + extraArgumentCount); | 110 parameters.parameterCount + extraArgumentCount); |
100 | 111 |
101 int count = 0; | 112 int count = 0; |
102 if (isInterceptedMethod) { | 113 if (isInterceptedMethod) { |
103 count++; | 114 count++; |
104 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 115 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
105 argumentsBuffer[0] = js('#', receiverArgumentName); | 116 argumentsBuffer[0] = js('#', receiverArgumentName); |
106 } | 117 } |
107 | 118 |
108 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 119 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
109 // Includes extra receiver argument when using interceptor convention | 120 // Includes extra receiver argument when using interceptor convention |
110 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | 121 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; |
111 | 122 |
112 parameters.orderedForEachParameter((ParameterElement element) { | 123 parameters.orderedForEachParameter((ParameterElement element) { |
113 String jsName = backend.namer.safeVariableName(element.name); | 124 String jsName = _namer.safeVariableName(element.name); |
114 assert(jsName != receiverArgumentName); | 125 assert(jsName != receiverArgumentName); |
115 if (count < optionalParameterStart) { | 126 if (count < optionalParameterStart) { |
116 parametersBuffer[count] = new jsAst.Parameter(jsName); | 127 parametersBuffer[count] = new jsAst.Parameter(jsName); |
117 argumentsBuffer[count] = js('#', jsName); | 128 argumentsBuffer[count] = js('#', jsName); |
118 } else { | 129 } else { |
119 int index = names.indexOf(element.name); | 130 int index = names.indexOf(element.name); |
120 if (index != -1) { | 131 if (index != -1) { |
121 indexOfLastOptionalArgumentInParameters = count; | 132 indexOfLastOptionalArgumentInParameters = count; |
122 // The order of the named arguments is not the same as the | 133 // The order of the named arguments is not the same as the |
123 // one in the real method (which is in Dart source order). | 134 // one in the real method (which is in Dart source order). |
124 argumentsBuffer[count] = js('#', jsName); | 135 argumentsBuffer[count] = js('#', jsName); |
125 parametersBuffer[optionalParameterStart + index] = | 136 parametersBuffer[optionalParameterStart + index] = |
126 new jsAst.Parameter(jsName); | 137 new jsAst.Parameter(jsName); |
127 } else { | 138 } else { |
128 ConstantValue value = handler.getConstantValue(element.constant); | 139 ConstantValue value = _constants.getConstantValue(element.constant); |
129 if (value == null) { | 140 if (value == null) { |
130 argumentsBuffer[count] = | 141 argumentsBuffer[count] = |
131 emitter.constantReference(new NullConstantValue()); | 142 _emitter.constantReference(new NullConstantValue()); |
132 } else { | 143 } else { |
133 if (!value.isNull) { | 144 if (!value.isNull) { |
134 // If the value is the null constant, we should not pass it | 145 // If the value is the null constant, we should not pass it |
135 // down to the native method. | 146 // down to the native method. |
136 indexOfLastOptionalArgumentInParameters = count; | 147 indexOfLastOptionalArgumentInParameters = count; |
137 } | 148 } |
138 argumentsBuffer[count] = emitter.constantReference(value); | 149 argumentsBuffer[count] = _emitter.constantReference(value); |
139 } | 150 } |
140 } | 151 } |
141 } | 152 } |
142 count++; | 153 count++; |
143 }); | 154 }); |
144 | 155 |
145 var body; // List or jsAst.Statement. | 156 var body; // List or jsAst.Statement. |
146 if (backend.nativeData.hasFixedBackendName(member)) { | 157 if (_nativeData.hasFixedBackendName(member)) { |
147 body = emitterTask.nativeEmitter.generateParameterStubStatements( | 158 body = _emitterTask.nativeEmitter.generateParameterStubStatements( |
148 member, | 159 member, |
149 isInterceptedMethod, | 160 isInterceptedMethod, |
150 namer.invocationName(selector), | 161 _namer.invocationName(selector), |
151 parametersBuffer, | 162 parametersBuffer, |
152 argumentsBuffer, | 163 argumentsBuffer, |
153 indexOfLastOptionalArgumentInParameters); | 164 indexOfLastOptionalArgumentInParameters); |
154 } else if (member.isInstanceMember) { | 165 } else if (member.isInstanceMember) { |
155 if (needsSuperGetter(member)) { | 166 if (needsSuperGetter(member)) { |
156 ClassElement superClass = member.enclosingClass; | 167 ClassElement superClass = member.enclosingClass; |
157 jsAst.Name methodName = namer.instanceMethodName(member); | 168 jsAst.Name methodName = _namer.instanceMethodName(member); |
158 // When redirecting, we must ensure that we don't end up in a subclass. | 169 // When redirecting, we must ensure that we don't end up in a subclass. |
159 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | 170 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. |
160 // Instead we need to call the statically resolved target. | 171 // Instead we need to call the statically resolved target. |
161 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | 172 // `<class>.prototype.bar$1.call(this, argument0, ...)`. |
162 body = js.statement('return #.#.call(this, #);', [ | 173 body = js.statement('return #.#.call(this, #);', [ |
163 backend.emitter | 174 _emitterTask.prototypeAccess(superClass, hasBeenInstantiated: true), |
164 .prototypeAccess(superClass, hasBeenInstantiated: true), | |
165 methodName, | 175 methodName, |
166 argumentsBuffer | 176 argumentsBuffer |
167 ]); | 177 ]); |
168 } else { | 178 } else { |
169 body = js.statement('return this.#(#);', | 179 body = js.statement('return this.#(#);', |
170 [namer.instanceMethodName(member), argumentsBuffer]); | 180 [_namer.instanceMethodName(member), argumentsBuffer]); |
171 } | 181 } |
172 } else { | 182 } else { |
173 body = js.statement('return #(#)', | 183 body = js.statement('return #(#)', |
174 [emitter.staticFunctionAccess(member), argumentsBuffer]); | 184 [_emitter.staticFunctionAccess(member), argumentsBuffer]); |
175 } | 185 } |
176 | 186 |
177 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); | 187 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); |
178 | 188 |
179 jsAst.Name name = member.isStatic ? null : namer.invocationName(selector); | 189 jsAst.Name name = member.isStatic ? null : _namer.invocationName(selector); |
180 jsAst.Name callName = | 190 jsAst.Name callName = |
181 (callSelector != null) ? namer.invocationName(callSelector) : null; | 191 (callSelector != null) ? _namer.invocationName(callSelector) : null; |
182 return new ParameterStubMethod(name, callName, function); | 192 return new ParameterStubMethod(name, callName, function); |
183 } | 193 } |
184 | 194 |
185 // We fill the lists depending on possible/invoked selectors. For example, | 195 // We fill the lists depending on possible/invoked selectors. For example, |
186 // take method foo: | 196 // take method foo: |
187 // foo(a, b, {c, d}); | 197 // foo(a, b, {c, d}); |
188 // | 198 // |
189 // We may have multiple ways of calling foo: | 199 // We may have multiple ways of calling foo: |
190 // (1) foo(1, 2); | 200 // (1) foo(1, 2); |
191 // (2) foo(1, 2, c: 3); | 201 // (2) foo(1, 2, c: 3); |
(...skipping 18 matching lines...) Expand all Loading... |
210 // We need to pay attention if this stub is for a function that has been | 220 // We need to pay attention if this stub is for a function that has been |
211 // invoked from a subclass. Then we cannot just redirect, since that | 221 // invoked from a subclass. Then we cannot just redirect, since that |
212 // would invoke the methods of the subclass. We have to compile to: | 222 // would invoke the methods of the subclass. We have to compile to: |
213 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) | 223 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) |
214 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); | 224 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); |
215 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); | 225 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); |
216 List<ParameterStubMethod> generateParameterStubs(MethodElement member, | 226 List<ParameterStubMethod> generateParameterStubs(MethodElement member, |
217 {bool canTearOff: true}) { | 227 {bool canTearOff: true}) { |
218 if (member.enclosingElement.isClosure) { | 228 if (member.enclosingElement.isClosure) { |
219 ClosureClassElement cls = member.enclosingElement; | 229 ClosureClassElement cls = member.enclosingElement; |
220 if (cls.supertype.element == backend.commonElements.boundClosureClass) { | 230 if (cls.supertype.element == _commonElements.boundClosureClass) { |
221 reporter.internalError(cls.methodElement, 'Bound closure1.'); | 231 throw new SpannableAssertionFailure( |
| 232 cls.methodElement, 'Bound closure1.'); |
222 } | 233 } |
223 if (cls.methodElement.isInstanceMember) { | 234 if (cls.methodElement.isInstanceMember) { |
224 reporter.internalError(cls.methodElement, 'Bound closure2.'); | 235 throw new SpannableAssertionFailure( |
| 236 cls.methodElement, 'Bound closure2.'); |
225 } | 237 } |
226 } | 238 } |
227 | 239 |
228 // The set of selectors that apply to `member`. For example, for | 240 // The set of selectors that apply to `member`. For example, for |
229 // a member `foo(x, [y])` the following selectors may apply: | 241 // a member `foo(x, [y])` the following selectors may apply: |
230 // `foo(x)`, and `foo(x, y)`. | 242 // `foo(x)`, and `foo(x, y)`. |
231 Map<Selector, SelectorConstraints> selectors; | 243 Map<Selector, SelectorConstraints> selectors; |
232 // The set of selectors that apply to `member` if it's name was `call`. | 244 // The set of selectors that apply to `member` if it's name was `call`. |
233 // This happens when a member is torn off. In that case calls to the | 245 // This happens when a member is torn off. In that case calls to the |
234 // function use the name `call`, and we must be able to handle every | 246 // function use the name `call`, and we must be able to handle every |
235 // `call` invocation that matches the signature. For example, for | 247 // `call` invocation that matches the signature. For example, for |
236 // a member `foo(x, [y])` the following selectors would be possible | 248 // a member `foo(x, [y])` the following selectors would be possible |
237 // call-selectors: `call(x)`, and `call(x, y)`. | 249 // call-selectors: `call(x)`, and `call(x, y)`. |
238 Map<Selector, SelectorConstraints> callSelectors; | 250 Map<Selector, SelectorConstraints> callSelectors; |
239 | 251 |
240 // Only instance members (not static methods) need stubs. | 252 // Only instance members (not static methods) need stubs. |
241 if (member.isInstanceMember) { | 253 if (member.isInstanceMember) { |
242 selectors = compiler.codegenWorldBuilder.invocationsByName(member.name); | 254 selectors = _codegenWorldBuilder.invocationsByName(member.name); |
243 } | 255 } |
244 | 256 |
245 if (canTearOff) { | 257 if (canTearOff) { |
246 String call = namer.closureInvocationSelectorName; | 258 String call = _namer.closureInvocationSelectorName; |
247 callSelectors = compiler.codegenWorldBuilder.invocationsByName(call); | 259 callSelectors = _codegenWorldBuilder.invocationsByName(call); |
248 } | 260 } |
249 | 261 |
250 assert(emptySelectorSet.isEmpty); | 262 assert(emptySelectorSet.isEmpty); |
251 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; | 263 if (selectors == null) selectors = const <Selector, SelectorConstraints>{}; |
252 if (callSelectors == null) | 264 if (callSelectors == null) |
253 callSelectors = const <Selector, SelectorConstraints>{}; | 265 callSelectors = const <Selector, SelectorConstraints>{}; |
254 | 266 |
255 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; | 267 List<ParameterStubMethod> stubs = <ParameterStubMethod>[]; |
256 | 268 |
257 if (selectors.isEmpty && callSelectors.isEmpty) { | 269 if (selectors.isEmpty && callSelectors.isEmpty) { |
(...skipping 29 matching lines...) Expand all Loading... |
287 } | 299 } |
288 } | 300 } |
289 } | 301 } |
290 | 302 |
291 // Now run through the actual member selectors (eg. `foo$2(x, y)` and not | 303 // Now run through the actual member selectors (eg. `foo$2(x, y)` and not |
292 // `call$2(x, y)`. Some of them have already been generated because of the | 304 // `call$2(x, y)`. Some of them have already been generated because of the |
293 // call-selectors (and they are in the renamedCallSelectors set. | 305 // call-selectors (and they are in the renamedCallSelectors set. |
294 for (Selector selector in selectors.keys) { | 306 for (Selector selector in selectors.keys) { |
295 if (renamedCallSelectors.contains(selector)) continue; | 307 if (renamedCallSelectors.contains(selector)) continue; |
296 if (!selector.appliesUnnamed(member)) continue; | 308 if (!selector.appliesUnnamed(member)) continue; |
297 if (!selectors[selector].applies(member, selector, closedWorld)) { | 309 if (!selectors[selector].applies(member, selector, _closedWorld)) { |
298 continue; | 310 continue; |
299 } | 311 } |
300 | 312 |
301 if (untypedSelectors.add(selector)) { | 313 if (untypedSelectors.add(selector)) { |
302 ParameterStubMethod stub = | 314 ParameterStubMethod stub = |
303 generateParameterStub(member, selector, null); | 315 generateParameterStub(member, selector, null); |
304 if (stub != null) { | 316 if (stub != null) { |
305 stubs.add(stub); | 317 stubs.add(stub); |
306 } | 318 } |
307 } | 319 } |
308 } | 320 } |
309 | 321 |
310 return stubs; | 322 return stubs; |
311 } | 323 } |
312 } | 324 } |
OLD | NEW |