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 '../common_elements.dart'; | 9 import '../common_elements.dart'; |
10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| 11 import '../elements/elements.dart' |
| 12 show |
| 13 ClassElement, |
| 14 FunctionElement, |
| 15 FunctionSignature, |
| 16 MethodElement, |
| 17 ParameterElement; |
11 import '../elements/entities.dart'; | 18 import '../elements/entities.dart'; |
12 import '../js/js.dart' as jsAst; | 19 import '../js/js.dart' as jsAst; |
13 import '../js/js.dart' show js; | 20 import '../js/js.dart' show js; |
| 21 import '../js_backend/constant_handler_javascript.dart' |
| 22 show JavaScriptConstantCompiler; |
14 import '../js_backend/namer.dart' show Namer; | 23 import '../js_backend/namer.dart' show Namer; |
15 import '../js_backend/native_data.dart'; | 24 import '../js_backend/native_data.dart'; |
16 import '../js_backend/interceptor_data.dart'; | 25 import '../js_backend/interceptor_data.dart'; |
17 import '../universe/call_structure.dart' show CallStructure; | 26 import '../universe/call_structure.dart' show CallStructure; |
18 import '../universe/selector.dart' show Selector; | 27 import '../universe/selector.dart' show Selector; |
19 import '../universe/world_builder.dart' | 28 import '../universe/world_builder.dart' |
20 show CodegenWorldBuilder, SelectorConstraints; | 29 show CodegenWorldBuilder, SelectorConstraints; |
21 import '../world.dart' show ClosedWorld; | 30 import '../world.dart' show ClosedWorld; |
22 | 31 |
23 import 'model.dart'; | 32 import 'model.dart'; |
24 | 33 |
25 import 'code_emitter_task.dart' show CodeEmitterTask, Emitter; | 34 import 'code_emitter_task.dart' show CodeEmitterTask, Emitter; |
26 | 35 |
27 class ParameterStubGenerator { | 36 class ParameterStubGenerator { |
28 static final Set<Selector> emptySelectorSet = new Set<Selector>(); | 37 static final Set<Selector> emptySelectorSet = new Set<Selector>(); |
29 | 38 |
30 final CommonElements _commonElements; | 39 final CommonElements _commonElements; |
31 final CodeEmitterTask _emitterTask; | 40 final CodeEmitterTask _emitterTask; |
| 41 final JavaScriptConstantCompiler _constants; |
32 final Namer _namer; | 42 final Namer _namer; |
33 final NativeData _nativeData; | 43 final NativeData _nativeData; |
34 final InterceptorData _interceptorData; | 44 final InterceptorData _interceptorData; |
35 final CodegenWorldBuilder _codegenWorldBuilder; | 45 final CodegenWorldBuilder _codegenWorldBuilder; |
36 final ClosedWorld _closedWorld; | 46 final ClosedWorld _closedWorld; |
37 | 47 |
38 ParameterStubGenerator( | 48 ParameterStubGenerator( |
39 this._commonElements, | 49 this._commonElements, |
40 this._emitterTask, | 50 this._emitterTask, |
| 51 this._constants, |
41 this._namer, | 52 this._namer, |
42 this._nativeData, | 53 this._nativeData, |
43 this._interceptorData, | 54 this._interceptorData, |
44 this._codegenWorldBuilder, | 55 this._codegenWorldBuilder, |
45 this._closedWorld); | 56 this._closedWorld); |
46 | 57 |
47 Emitter get _emitter => _emitterTask.emitter; | 58 Emitter get _emitter => _emitterTask.emitter; |
48 | 59 |
49 bool needsSuperGetter(FunctionEntity element) => | 60 bool needsSuperGetter(FunctionElement element) => |
50 _codegenWorldBuilder.methodsNeedingSuperGetter.contains(element); | 61 _codegenWorldBuilder.methodsNeedingSuperGetter.contains(element); |
51 | 62 |
52 /** | 63 /** |
53 * Generates stubs to handle invocation of methods with optional | 64 * Generates stubs to handle invocation of methods with optional |
54 * arguments. | 65 * arguments. |
55 * | 66 * |
56 * A method like `foo([x])` may be invoked by the following | 67 * A method like `foo([x])` may be invoked by the following |
57 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the | 68 * calls: `foo(), foo(1), foo(x: 1)`. This method generates the stub for the |
58 * given [selector] and returns the generated [ParameterStubMethod]. | 69 * given [selector] and returns the generated [ParameterStubMethod]. |
59 * | 70 * |
60 * Returns null if no stub is needed. | 71 * Returns null if no stub is needed. |
61 * | 72 * |
62 * Members may be invoked in two ways: directly, or through a closure. In the | 73 * Members may be invoked in two ways: directly, or through a closure. In the |
63 * latter case the caller invokes the closure's `call` method. This method | 74 * latter case the caller invokes the closure's `call` method. This method |
64 * accepts two selectors. The returned stub method has the corresponding | 75 * accepts two selectors. The returned stub method has the corresponding |
65 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if | 76 * name [ParameterStubMethod.name] and [ParameterStubMethod.callName] set if |
66 * the input selector is non-null (and the member needs a stub). | 77 * the input selector is non-null (and the member needs a stub). |
67 */ | 78 */ |
68 ParameterStubMethod generateParameterStub( | 79 ParameterStubMethod generateParameterStub( |
69 FunctionEntity member, Selector selector, Selector callSelector) { | 80 MethodElement member, Selector selector, Selector callSelector) { |
70 CallStructure callStructure = selector.callStructure; | 81 CallStructure callStructure = selector.callStructure; |
71 ParameterStructure parameterStructure = member.parameterStructure; | 82 FunctionSignature parameters = member.functionSignature; |
72 int positionalArgumentCount = callStructure.positionalArgumentCount; | 83 int positionalArgumentCount = callStructure.positionalArgumentCount; |
73 if (positionalArgumentCount == parameterStructure.totalParameters) { | 84 if (positionalArgumentCount == parameters.parameterCount) { |
74 assert(callStructure.isUnnamed); | 85 assert(callStructure.isUnnamed); |
75 return null; | 86 return null; |
76 } | 87 } |
77 if (parameterStructure.namedParameters.isNotEmpty && | 88 if (parameters.optionalParametersAreNamed && |
78 callStructure.namedArgumentCount == | 89 callStructure.namedArgumentCount == parameters.optionalParameterCount) { |
79 parameterStructure.namedParameters.length) { | |
80 // If the selector has the same number of named arguments as the element, | 90 // If the selector has the same number of named arguments as the element, |
81 // we don't need to add a stub. The call site will hit the method | 91 // we don't need to add a stub. The call site will hit the method |
82 // directly. | 92 // directly. |
83 return null; | 93 return null; |
84 } | 94 } |
85 | |
86 List<String> names = callStructure.getOrderedNamedArguments(); | 95 List<String> names = callStructure.getOrderedNamedArguments(); |
87 | 96 |
88 bool isInterceptedMethod = _interceptorData.isInterceptedMethod(member); | 97 bool isInterceptedMethod = _interceptorData.isInterceptedMethod(member); |
89 | 98 |
90 // If the method is intercepted, we need to also pass the actual receiver. | 99 // If the method is intercepted, we need to also pass the actual receiver. |
91 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | 100 int extraArgumentCount = isInterceptedMethod ? 1 : 0; |
92 // Use '$receiver' to avoid clashes with other parameter names. Using | 101 // Use '$receiver' to avoid clashes with other parameter names. Using |
93 // '$receiver' works because namer.safeVariableName used for getting | 102 // '$receiver' works because namer.safeVariableName used for getting |
94 // parameter names never returns a name beginning with a single '$'. | 103 // parameter names never returns a name beginning with a single '$'. |
95 String receiverArgumentName = r'$receiver'; | 104 String receiverArgumentName = r'$receiver'; |
96 | 105 |
97 // The parameters that this stub takes. | 106 // The parameters that this stub takes. |
98 List<jsAst.Parameter> parametersBuffer = | 107 List<jsAst.Parameter> parametersBuffer = |
99 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | 108 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); |
100 // The arguments that will be passed to the real method. | 109 // The arguments that will be passed to the real method. |
101 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( | 110 List<jsAst.Expression> argumentsBuffer = new List<jsAst.Expression>( |
102 parameterStructure.totalParameters + extraArgumentCount); | 111 parameters.parameterCount + extraArgumentCount); |
103 | 112 |
104 int count = 0; | 113 int count = 0; |
105 if (isInterceptedMethod) { | 114 if (isInterceptedMethod) { |
106 count++; | 115 count++; |
107 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | 116 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); |
108 argumentsBuffer[0] = js('#', receiverArgumentName); | 117 argumentsBuffer[0] = js('#', receiverArgumentName); |
109 } | 118 } |
110 | 119 |
111 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 120 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
112 // Includes extra receiver argument when using interceptor convention | 121 // Includes extra receiver argument when using interceptor convention |
113 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | 122 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; |
114 | 123 |
115 _codegenWorldBuilder.forEachParameter(member, | 124 parameters.orderedForEachParameter((_element) { |
116 (_, String name, ConstantValue value) { | 125 ParameterElement element = _element; |
117 String jsName = _namer.safeVariableName(name); | 126 String jsName = _namer.safeVariableName(element.name); |
118 assert(jsName != receiverArgumentName); | 127 assert(jsName != receiverArgumentName); |
119 if (count < optionalParameterStart) { | 128 if (count < optionalParameterStart) { |
120 parametersBuffer[count] = new jsAst.Parameter(jsName); | 129 parametersBuffer[count] = new jsAst.Parameter(jsName); |
121 argumentsBuffer[count] = js('#', jsName); | 130 argumentsBuffer[count] = js('#', jsName); |
122 } else { | 131 } else { |
123 int index = names.indexOf(name); | 132 int index = names.indexOf(element.name); |
124 if (index != -1) { | 133 if (index != -1) { |
125 indexOfLastOptionalArgumentInParameters = count; | 134 indexOfLastOptionalArgumentInParameters = count; |
126 // The order of the named arguments is not the same as the | 135 // The order of the named arguments is not the same as the |
127 // one in the real method (which is in Dart source order). | 136 // one in the real method (which is in Dart source order). |
128 argumentsBuffer[count] = js('#', jsName); | 137 argumentsBuffer[count] = js('#', jsName); |
129 parametersBuffer[optionalParameterStart + index] = | 138 parametersBuffer[optionalParameterStart + index] = |
130 new jsAst.Parameter(jsName); | 139 new jsAst.Parameter(jsName); |
131 } else { | 140 } else { |
| 141 ConstantValue value = _constants.getConstantValue(element.constant); |
132 if (value == null) { | 142 if (value == null) { |
133 argumentsBuffer[count] = | 143 argumentsBuffer[count] = |
134 _emitter.constantReference(new NullConstantValue()); | 144 _emitter.constantReference(new NullConstantValue()); |
135 } else { | 145 } else { |
136 if (!value.isNull) { | 146 if (!value.isNull) { |
137 // If the value is the null constant, we should not pass it | 147 // If the value is the null constant, we should not pass it |
138 // down to the native method. | 148 // down to the native method. |
139 indexOfLastOptionalArgumentInParameters = count; | 149 indexOfLastOptionalArgumentInParameters = count; |
140 } | 150 } |
141 argumentsBuffer[count] = _emitter.constantReference(value); | 151 argumentsBuffer[count] = _emitter.constantReference(value); |
142 } | 152 } |
143 } | 153 } |
144 } | 154 } |
145 count++; | 155 count++; |
146 }); | 156 }); |
147 | 157 |
148 var body; // List or jsAst.Statement. | 158 var body; // List or jsAst.Statement. |
149 if (_nativeData.hasFixedBackendName(member)) { | 159 if (_nativeData.hasFixedBackendName(member)) { |
150 body = _emitterTask.nativeEmitter.generateParameterStubStatements( | 160 body = _emitterTask.nativeEmitter.generateParameterStubStatements( |
151 member, | 161 member, |
152 isInterceptedMethod, | 162 isInterceptedMethod, |
153 _namer.invocationName(selector), | 163 _namer.invocationName(selector), |
154 parametersBuffer, | 164 parametersBuffer, |
155 argumentsBuffer, | 165 argumentsBuffer, |
156 indexOfLastOptionalArgumentInParameters); | 166 indexOfLastOptionalArgumentInParameters); |
157 } else if (member.isInstanceMember) { | 167 } else if (member.isInstanceMember) { |
158 if (needsSuperGetter(member)) { | 168 if (needsSuperGetter(member)) { |
159 ClassEntity superClass = member.enclosingClass; | 169 ClassElement superClass = member.enclosingClass; |
160 jsAst.Name methodName = _namer.instanceMethodName(member); | 170 jsAst.Name methodName = _namer.instanceMethodName(member); |
161 // When redirecting, we must ensure that we don't end up in a subclass. | 171 // When redirecting, we must ensure that we don't end up in a subclass. |
162 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | 172 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. |
163 // Instead we need to call the statically resolved target. | 173 // Instead we need to call the statically resolved target. |
164 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | 174 // `<class>.prototype.bar$1.call(this, argument0, ...)`. |
165 body = js.statement('return #.#.call(this, #);', [ | 175 body = js.statement('return #.#.call(this, #);', [ |
166 _emitterTask.prototypeAccess(superClass, hasBeenInstantiated: true), | 176 _emitterTask.prototypeAccess(superClass, hasBeenInstantiated: true), |
167 methodName, | 177 methodName, |
168 argumentsBuffer | 178 argumentsBuffer |
169 ]); | 179 ]); |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
307 generateParameterStub(member, selector, null); | 317 generateParameterStub(member, selector, null); |
308 if (stub != null) { | 318 if (stub != null) { |
309 stubs.add(stub); | 319 stubs.add(stub); |
310 } | 320 } |
311 } | 321 } |
312 } | 322 } |
313 | 323 |
314 return stubs; | 324 return stubs; |
315 } | 325 } |
316 } | 326 } |
OLD | NEW |