| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 /// This class should morph into something that makes it easy to build | 7 /// This class should morph into something that makes it easy to build |
| 8 /// JavaScript representations of libraries, class-sides, and instance-sides. | 8 /// JavaScript representations of libraries, class-sides, and instance-sides. |
| 9 /// Initially, it is just a placeholder for code that is moved from | 9 /// Initially, it is just a placeholder for code that is moved from |
| 10 /// [CodeEmitterTask]. | 10 /// [CodeEmitterTask]. |
| 11 class ContainerBuilder extends CodeEmitterHelper { | 11 class ContainerBuilder extends CodeEmitterHelper { |
| 12 bool needsSuperGetter(FunctionElement element) => | |
| 13 compiler.codegenWorld.methodsNeedingSuperGetter.contains(element); | |
| 14 | |
| 15 /** | |
| 16 * Generate stubs to handle invocation of methods with optional | |
| 17 * arguments. | |
| 18 * | |
| 19 * A method like [: foo([x]) :] may be invoked by the following | |
| 20 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this | |
| 21 * function for detailed examples. | |
| 22 */ | |
| 23 void addParameterStub(FunctionElement member, | |
| 24 Selector selector, | |
| 25 AddStubFunction addStub) { | |
| 26 FunctionSignature parameters = member.functionSignature; | |
| 27 int positionalArgumentCount = selector.positionalArgumentCount; | |
| 28 if (positionalArgumentCount == parameters.parameterCount) { | |
| 29 assert(selector.namedArgumentCount == 0); | |
| 30 return; | |
| 31 } | |
| 32 if (parameters.optionalParametersAreNamed | |
| 33 && selector.namedArgumentCount == parameters.optionalParameterCount) { | |
| 34 // If the selector has the same number of named arguments as the element, | |
| 35 // we don't need to add a stub. The call site will hit the method | |
| 36 // directly. | |
| 37 return; | |
| 38 } | |
| 39 JavaScriptConstantCompiler handler = backend.constants; | |
| 40 List<String> names = selector.getOrderedNamedArguments(); | |
| 41 | |
| 42 bool isInterceptedMethod = backend.isInterceptedMethod(member); | |
| 43 | |
| 44 // If the method is intercepted, we need to also pass the actual receiver. | |
| 45 int extraArgumentCount = isInterceptedMethod ? 1 : 0; | |
| 46 // Use '$receiver' to avoid clashes with other parameter names. Using | |
| 47 // '$receiver' works because [:namer.safeName:] used for getting parameter | |
| 48 // names never returns a name beginning with a single '$'. | |
| 49 String receiverArgumentName = r'$receiver'; | |
| 50 | |
| 51 // The parameters that this stub takes. | |
| 52 List<jsAst.Parameter> parametersBuffer = | |
| 53 new List<jsAst.Parameter>(selector.argumentCount + extraArgumentCount); | |
| 54 // The arguments that will be passed to the real method. | |
| 55 List<jsAst.Expression> argumentsBuffer = | |
| 56 new List<jsAst.Expression>( | |
| 57 parameters.parameterCount + extraArgumentCount); | |
| 58 String invocationName = namer.invocationName(selector); | |
| 59 | |
| 60 int count = 0; | |
| 61 if (isInterceptedMethod) { | |
| 62 count++; | |
| 63 parametersBuffer[0] = new jsAst.Parameter(receiverArgumentName); | |
| 64 argumentsBuffer[0] = js('#', receiverArgumentName); | |
| 65 } | |
| 66 | |
| 67 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | |
| 68 // Includes extra receiver argument when using interceptor convention | |
| 69 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | |
| 70 | |
| 71 int parameterIndex = 0; | |
| 72 parameters.orderedForEachParameter((ParameterElement element) { | |
| 73 String jsName = backend.namer.safeName(element.name); | |
| 74 assert(jsName != receiverArgumentName); | |
| 75 if (count < optionalParameterStart) { | |
| 76 parametersBuffer[count] = new jsAst.Parameter(jsName); | |
| 77 argumentsBuffer[count] = js('#', jsName); | |
| 78 } else { | |
| 79 int index = names.indexOf(element.name); | |
| 80 if (index != -1) { | |
| 81 indexOfLastOptionalArgumentInParameters = count; | |
| 82 // The order of the named arguments is not the same as the | |
| 83 // one in the real method (which is in Dart source order). | |
| 84 argumentsBuffer[count] = js('#', jsName); | |
| 85 parametersBuffer[optionalParameterStart + index] = | |
| 86 new jsAst.Parameter(jsName); | |
| 87 } else { | |
| 88 ConstantExpression constant = handler.getConstantForVariable(element); | |
| 89 if (constant == null) { | |
| 90 argumentsBuffer[count] = | |
| 91 emitter.constantReference(new NullConstantValue()); | |
| 92 } else { | |
| 93 ConstantValue value = constant.value; | |
| 94 if (!value.isNull) { | |
| 95 // If the value is the null constant, we should not pass it | |
| 96 // down to the native method. | |
| 97 indexOfLastOptionalArgumentInParameters = count; | |
| 98 } | |
| 99 argumentsBuffer[count] = emitter.constantReference(value); | |
| 100 } | |
| 101 } | |
| 102 } | |
| 103 count++; | |
| 104 }); | |
| 105 | |
| 106 var body; // List or jsAst.Statement. | |
| 107 if (member.hasFixedBackendName) { | |
| 108 body = emitter.nativeEmitter.generateParameterStubStatements( | |
| 109 member, isInterceptedMethod, invocationName, | |
| 110 parametersBuffer, argumentsBuffer, | |
| 111 indexOfLastOptionalArgumentInParameters); | |
| 112 } else if (member.isInstanceMember) { | |
| 113 if (needsSuperGetter(member)) { | |
| 114 ClassElement superClass = member.enclosingClass; | |
| 115 String methodName = namer.getNameOfInstanceMember(member); | |
| 116 // When redirecting, we must ensure that we don't end up in a subclass. | |
| 117 // We thus can't just invoke `this.foo$1.call(filledInArguments)`. | |
| 118 // Instead we need to call the statically resolved target. | |
| 119 // `<class>.prototype.bar$1.call(this, argument0, ...)`. | |
| 120 body = js.statement( | |
| 121 'return #.#.call(this, #);', | |
| 122 [backend.emitter.prototypeAccess(superClass, | |
| 123 hasBeenInstantiated: true), | |
| 124 methodName, | |
| 125 argumentsBuffer]); | |
| 126 } else { | |
| 127 body = js.statement( | |
| 128 'return this.#(#);', | |
| 129 [namer.getNameOfInstanceMember(member), argumentsBuffer]); | |
| 130 } | |
| 131 } else { | |
| 132 body = js.statement('return #(#)', | |
| 133 [emitter.staticFunctionAccess(member), argumentsBuffer]); | |
| 134 } | |
| 135 | |
| 136 jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]); | |
| 137 | |
| 138 addStub(selector, function); | |
| 139 } | |
| 140 | |
| 141 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, | |
| 142 [bool canTearOff = false]) { | |
| 143 if (member.enclosingElement.isClosure) { | |
| 144 ClosureClassElement cls = member.enclosingElement; | |
| 145 if (cls.supertype.element == backend.boundClosureClass) { | |
| 146 compiler.internalError(cls.methodElement, 'Bound closure1.'); | |
| 147 } | |
| 148 if (cls.methodElement.isInstanceMember) { | |
| 149 compiler.internalError(cls.methodElement, 'Bound closure2.'); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 // We fill the lists depending on the selector. For example, | |
| 154 // take method foo: | |
| 155 // foo(a, b, {c, d}); | |
| 156 // | |
| 157 // We may have multiple ways of calling foo: | |
| 158 // (1) foo(1, 2); | |
| 159 // (2) foo(1, 2, c: 3); | |
| 160 // (3) foo(1, 2, d: 4); | |
| 161 // (4) foo(1, 2, c: 3, d: 4); | |
| 162 // (5) foo(1, 2, d: 4, c: 3); | |
| 163 // | |
| 164 // What we generate at the call sites are: | |
| 165 // (1) foo$2(1, 2); | |
| 166 // (2) foo$3$c(1, 2, 3); | |
| 167 // (3) foo$3$d(1, 2, 4); | |
| 168 // (4) foo$4$c$d(1, 2, 3, 4); | |
| 169 // (5) foo$4$c$d(1, 2, 3, 4); | |
| 170 // | |
| 171 // The stubs we generate are (expressed in Dart): | |
| 172 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | |
| 173 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); | |
| 174 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); | |
| 175 // (4) No stub generated, call is direct. | |
| 176 // (5) No stub generated, call is direct. | |
| 177 // | |
| 178 // We need to pay attention if this stub is for a function that has been | |
| 179 // invoked from a subclass. Then we cannot just redirect, since that | |
| 180 // would invoke the methods of the subclass. We have to compile to: | |
| 181 // (1) foo$2(a, b) => MyClass.foo$4$c$d.call(this, a, b, null, null) | |
| 182 // (2) foo$3$c(a, b, c) => MyClass.foo$4$c$d(this, a, b, c, null); | |
| 183 // (3) foo$3$d(a, b, d) => MyClass.foo$4$c$d(this, a, b, null, d); | |
| 184 | |
| 185 Set<Selector> selectors = member.isInstanceMember | |
| 186 ? compiler.codegenWorld.invokedNames[member.name] | |
| 187 : null; // No stubs needed for static methods. | |
| 188 | |
| 189 /// Returns all closure call selectors renamed to match this member. | |
| 190 Set<Selector> callSelectorsAsNamed() { | |
| 191 if (!canTearOff) return null; | |
| 192 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
| 193 namer.closureInvocationSelectorName]; | |
| 194 if (callSelectors == null) return null; | |
| 195 return callSelectors.map((Selector callSelector) { | |
| 196 return new Selector.call( | |
| 197 member.name, member.library, | |
| 198 callSelector.argumentCount, callSelector.namedArguments); | |
| 199 }).toSet(); | |
| 200 } | |
| 201 if (selectors == null) { | |
| 202 selectors = callSelectorsAsNamed(); | |
| 203 if (selectors == null) return; | |
| 204 } else { | |
| 205 Set<Selector> callSelectors = callSelectorsAsNamed(); | |
| 206 if (callSelectors != null) { | |
| 207 selectors = selectors.union(callSelectors); | |
| 208 } | |
| 209 } | |
| 210 Set<Selector> untypedSelectors = new Set<Selector>(); | |
| 211 if (selectors != null) { | |
| 212 for (Selector selector in selectors) { | |
| 213 if (!selector.appliesUnnamed(member, compiler.world)) continue; | |
| 214 if (untypedSelectors.add(selector.asUntyped)) { | |
| 215 addParameterStub(member, selector, defineStub); | |
| 216 } | |
| 217 } | |
| 218 } | |
| 219 if (canTearOff) { | |
| 220 selectors = compiler.codegenWorld.invokedNames[ | |
| 221 namer.closureInvocationSelectorName]; | |
| 222 if (selectors != null) { | |
| 223 for (Selector selector in selectors) { | |
| 224 selector = new Selector.call( | |
| 225 member.name, member.library, | |
| 226 selector.argumentCount, selector.namedArguments); | |
| 227 if (!selector.appliesUnnamed(member, compiler.world)) continue; | |
| 228 if (untypedSelectors.add(selector)) { | |
| 229 addParameterStub(member, selector, defineStub); | |
| 230 } | |
| 231 } | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | 12 |
| 236 void addMemberMethod(DartMethod method, ClassBuilder builder) { | 13 void addMemberMethod(DartMethod method, ClassBuilder builder) { |
| 237 final FunctionElement member = method.element; | 14 final FunctionElement member = method.element; |
| 238 String name = method.name; | 15 String name = method.name; |
| 239 final FunctionSignature parameters = member.functionSignature; | 16 final FunctionSignature parameters = member.functionSignature; |
| 240 jsAst.Expression code = method.code; | 17 jsAst.Expression code = method.code; |
| 241 final bool needsStubs = method.needsStubs; | 18 final bool needsStubs = method.parameterStubs.isNotEmpty; |
| 242 final bool canTearOff = method.needsTearOff; | 19 final bool canTearOff = method.needsTearOff; |
| 243 final String tearOffName = method.tearOffName; | 20 final String tearOffName = method.tearOffName; |
| 244 final bool canBeReflected = method.canBeReflected; | 21 final bool canBeReflected = method.canBeReflected; |
| 245 final bool canBeApplied = method.canBeApplied; | 22 final bool canBeApplied = method.canBeApplied; |
| 246 final bool isClosure = method is InstanceMethod && method.isClosure; | 23 final bool isClosure = method is InstanceMethod && method.isClosure; |
| 247 final bool hasSuperAlias = method is InstanceMethod && method.hasSuperAlias; | 24 final bool hasSuperAlias = method is InstanceMethod && method.hasSuperAlias; |
| 248 | 25 |
| 249 final bool needStructuredInfo = | 26 final bool needStructuredInfo = |
| 250 canTearOff || canBeReflected || canBeApplied || hasSuperAlias; | 27 canTearOff || canBeReflected || canBeApplied || hasSuperAlias; |
| 251 | 28 |
| 252 emitter.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); | 29 emitter.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); |
| 253 | 30 |
| 254 if (!needStructuredInfo) { | 31 if (!needStructuredInfo) { |
| 255 compiler.dumpInfoTask.registerElementAst(member, | 32 compiler.dumpInfoTask.registerElementAst(member, |
| 256 builder.addProperty(name, code)); | 33 builder.addProperty(name, code)); |
| 257 if (needsStubs) { | 34 |
| 258 addParameterStubs( | 35 for (ParameterStubMethod method in method.parameterStubs) { |
| 259 member, | 36 jsAst.Property property = builder.addProperty(method.name, method.code); |
| 260 (Selector selector, jsAst.Fun function) { | 37 compiler.dumpInfoTask.registerElementAst(member, property); |
| 261 String invocationName = namer.invocationName(selector); | 38 emitter.interceptorEmitter |
| 262 emitter.interceptorEmitter | 39 .recordMangledNameOfMemberMethod(member, method.name); |
| 263 .recordMangledNameOfMemberMethod(member, invocationName); | |
| 264 compiler.dumpInfoTask.registerElementAst(member, | |
| 265 builder.addProperty(invocationName, function)); | |
| 266 }); | |
| 267 } | 40 } |
| 268 return; | 41 return; |
| 269 } | 42 } |
| 270 emitter.needsStructuredMemberInfo = true; | 43 emitter.needsStructuredMemberInfo = true; |
| 271 | 44 |
| 272 // This element is needed for reflection or needs additional stubs or has a | 45 // This element is needed for reflection or needs additional stubs or has a |
| 273 // super alias. So we need to retain additional information. | 46 // super alias. So we need to retain additional information. |
| 274 | 47 |
| 275 // The information is stored in an array with this format: | 48 // The information is stored in an array with this format: |
| 276 // | 49 // |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 int requiredParameterCount = parameters.requiredParameterCount << 1; | 102 int requiredParameterCount = parameters.requiredParameterCount << 1; |
| 330 if (member.isAccessor) requiredParameterCount++; | 103 if (member.isAccessor) requiredParameterCount++; |
| 331 | 104 |
| 332 int optionalParameterCount = parameters.optionalParameterCount << 1; | 105 int optionalParameterCount = parameters.optionalParameterCount << 1; |
| 333 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | 106 if (parameters.optionalParametersAreNamed) optionalParameterCount++; |
| 334 | 107 |
| 335 // TODO(sra): Don't use LiteralString for non-strings. | 108 // TODO(sra): Don't use LiteralString for non-strings. |
| 336 List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; | 109 List tearOffInfo = [new jsAst.LiteralString(callSelectorString)]; |
| 337 | 110 |
| 338 if (needsStubs || canTearOff) { | 111 if (needsStubs || canTearOff) { |
| 339 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
| 340 | 112 |
| 341 String invocationName = namer.invocationName(selector); | 113 for (ParameterStubMethod method in method.parameterStubs) { |
| 342 emitter.interceptorEmitter. | 114 String invocationName = method.name; |
| 343 recordMangledNameOfMemberMethod(member, invocationName); | 115 emitter.interceptorEmitter |
| 344 expressions.add(function); | 116 .recordMangledNameOfMemberMethod(member, invocationName); |
| 117 expressions.add(method.code); |
| 345 if (member.isInstanceMember) { | 118 if (member.isInstanceMember) { |
| 346 Set invokedSelectors = | 119 expressions.add(js.string(invocationName)); |
| 347 compiler.codegenWorld.invokedNames[member.name]; | |
| 348 expressions.add(js.string(invocationName)); | |
| 349 } else { | 120 } else { |
| 350 expressions.add(js('null')); | 121 expressions.add(js('null')); |
| 351 // TOOD(ahe): Since we know when reading static data versus instance | 122 // TOOD(ahe): Since we know when reading static data versus instance |
| 352 // data, we can eliminate this element. | 123 // data, we can eliminate this element. |
| 353 } | 124 } |
| 125 |
| 354 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | 126 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
| 355 namer.closureInvocationSelectorName]; | 127 namer.closureInvocationSelectorName]; |
| 356 Selector callSelector = selector.toCallSelector(); | 128 Selector callSelector = method.selector.toCallSelector(); |
| 357 String callSelectorString = 'null'; | 129 String callSelectorString = 'null'; |
| 358 if (canTearOff && callSelectors != null && | 130 if (canTearOff && callSelectors != null && |
| 359 callSelectors.contains(callSelector)) { | 131 callSelectors.contains(callSelector)) { |
| 360 callSelectorString = '"${namer.invocationName(callSelector)}"'; | 132 callSelectorString = '"${namer.invocationName(callSelector)}"'; |
| 361 } | 133 } |
| 362 tearOffInfo.add(new jsAst.LiteralString(callSelectorString)); | 134 tearOffInfo.add(new jsAst.LiteralString(callSelectorString)); |
| 363 }, canTearOff); | 135 } |
| 364 } | 136 } |
| 365 | 137 |
| 366 jsAst.Expression memberTypeExpression; | 138 jsAst.Expression memberTypeExpression; |
| 367 if (canTearOff || canBeReflected) { | 139 if (canTearOff || canBeReflected) { |
| 368 DartType memberType; | 140 DartType memberType; |
| 369 if (member.isGenerativeConstructorBody) { | 141 if (member.isGenerativeConstructorBody) { |
| 370 var body = member; | 142 var body = member; |
| 371 memberType = body.constructor.type; | 143 memberType = body.constructor.type; |
| 372 } else { | 144 } else { |
| 373 memberType = member.type; | 145 memberType = member.type; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 434 jsAst.ArrayInitializer arrayInit = | 206 jsAst.ArrayInitializer arrayInit = |
| 435 new jsAst.ArrayInitializer(expressions.toList()); | 207 new jsAst.ArrayInitializer(expressions.toList()); |
| 436 compiler.dumpInfoTask.registerElementAst(member, | 208 compiler.dumpInfoTask.registerElementAst(member, |
| 437 builder.addProperty(name, arrayInit)); | 209 builder.addProperty(name, arrayInit)); |
| 438 } | 210 } |
| 439 | 211 |
| 440 void addMemberField(Field field, ClassBuilder builder) { | 212 void addMemberField(Field field, ClassBuilder builder) { |
| 441 // For now, do nothing. | 213 // For now, do nothing. |
| 442 } | 214 } |
| 443 } | 215 } |
| OLD | NEW |