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