| 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 |