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