| 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 final Map<Element, Element> staticGetters = new Map<Element, Element>(); | 12 final Map<Element, Element> staticGetters = new Map<Element, Element>(); |
| 13 | 13 |
| 14 /// A cache of synthesized closures for top-level, static or | 14 /// A cache of synthesized closures for top-level, static or |
| 15 /// instance methods. | 15 /// instance methods. |
| 16 final Map<String, Element> methodClosures = <String, Element>{}; | 16 final Map<String, Element> methodClosures = <String, Element>{}; |
| 17 | 17 |
| 18 /** | 18 /** |
| 19 * Generate stubs to handle invocation of methods with optional | 19 * Generate stubs to handle invocation of methods with optional |
| 20 * arguments. | 20 * arguments. |
| 21 * | 21 * |
| 22 * A method like [: foo([x]) :] may be invoked by the following | 22 * A method like [: foo([x]) :] may be invoked by the following |
| 23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this | 23 * calls: [: foo(), foo(1), foo(x: 1) :]. See the sources of this |
| 24 * function for detailed examples. | 24 * function for detailed examples. |
| 25 */ | 25 */ |
| 26 void addParameterStub(FunctionElement member, | 26 void addParameterStub(FunctionElement member, |
| 27 Selector selector, | 27 Selector selector, |
| 28 AddStubFunction addStub, | 28 DefineStubFunction defineStub, |
| 29 Set<String> alreadyGenerated) { | 29 Set<String> alreadyGenerated) { |
| 30 FunctionSignature parameters = member.computeSignature(compiler); | 30 FunctionSignature parameters = member.computeSignature(compiler); |
| 31 int positionalArgumentCount = selector.positionalArgumentCount; | 31 int positionalArgumentCount = selector.positionalArgumentCount; |
| 32 if (positionalArgumentCount == parameters.parameterCount) { | 32 if (positionalArgumentCount == parameters.parameterCount) { |
| 33 assert(selector.namedArgumentCount == 0); | 33 assert(selector.namedArgumentCount == 0); |
| 34 return; | 34 return; |
| 35 } | 35 } |
| 36 if (parameters.optionalParametersAreNamed | 36 if (parameters.optionalParametersAreNamed |
| 37 && selector.namedArgumentCount == parameters.optionalParameterCount) { | 37 && selector.namedArgumentCount == parameters.optionalParameterCount) { |
| 38 // If the selector has the same number of named arguments as the element, | 38 // If the selector has the same number of named arguments as the element, |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 | 74 |
| 75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; | 75 int optionalParameterStart = positionalArgumentCount + extraArgumentCount; |
| 76 // Includes extra receiver argument when using interceptor convention | 76 // Includes extra receiver argument when using interceptor convention |
| 77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; | 77 int indexOfLastOptionalArgumentInParameters = optionalParameterStart - 1; |
| 78 | 78 |
| 79 TreeElements elements = | 79 TreeElements elements = |
| 80 compiler.enqueuer.resolution.getCachedElements(member); | 80 compiler.enqueuer.resolution.getCachedElements(member); |
| 81 | 81 |
| 82 int parameterIndex = 0; | 82 int parameterIndex = 0; |
| 83 parameters.orderedForEachParameter((Element element) { | 83 parameters.orderedForEachParameter((Element element) { |
| 84 String jsName = backend.namer.safeName(element.name); | 84 // Use generic names for closures to facilitate code sharing. |
| 85 String jsName = member is ClosureInvocationElement |
| 86 ? 'p${parameterIndex++}' |
| 87 : backend.namer.safeName(element.name); |
| 85 assert(jsName != receiverArgumentName); | 88 assert(jsName != receiverArgumentName); |
| 86 if (count < optionalParameterStart) { | 89 if (count < optionalParameterStart) { |
| 87 parametersBuffer[count] = new jsAst.Parameter(jsName); | 90 parametersBuffer[count] = new jsAst.Parameter(jsName); |
| 88 argumentsBuffer[count] = js(jsName); | 91 argumentsBuffer[count] = js(jsName); |
| 89 } else { | 92 } else { |
| 90 int index = names.indexOf(element.name); | 93 int index = names.indexOf(element.name); |
| 91 if (index != -1) { | 94 if (index != -1) { |
| 92 indexOfLastOptionalArgumentInParameters = count; | 95 indexOfLastOptionalArgumentInParameters = count; |
| 93 // The order of the named arguments is not the same as the | 96 // The order of the named arguments is not the same as the |
| 94 // one in the real method (which is in Dart source order). | 97 // one in the real method (which is in Dart source order). |
| (...skipping 16 matching lines...) Expand all Loading... |
| 111 } | 114 } |
| 112 count++; | 115 count++; |
| 113 }); | 116 }); |
| 114 | 117 |
| 115 List body; | 118 List body; |
| 116 if (member.hasFixedBackendName()) { | 119 if (member.hasFixedBackendName()) { |
| 117 body = task.nativeEmitter.generateParameterStubStatements( | 120 body = task.nativeEmitter.generateParameterStubStatements( |
| 118 member, isInterceptedMethod, invocationName, | 121 member, isInterceptedMethod, invocationName, |
| 119 parametersBuffer, argumentsBuffer, | 122 parametersBuffer, argumentsBuffer, |
| 120 indexOfLastOptionalArgumentInParameters); | 123 indexOfLastOptionalArgumentInParameters); |
| 121 } else if (member.isInstanceMember()) { | 124 } else { |
| 122 body = [js.return_( | 125 body = [js.return_( |
| 123 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; | 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; |
| 124 } else { | |
| 125 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; | |
| 126 } | 127 } |
| 127 | 128 |
| 128 jsAst.Fun function = js.fun(parametersBuffer, body); | 129 jsAst.Fun function = js.fun(parametersBuffer, body); |
| 129 | 130 |
| 130 addStub(selector, function); | 131 defineStub(invocationName, function); |
| 132 |
| 133 String reflectionName = task.getReflectionName(selector, invocationName); |
| 134 if (reflectionName != null) { |
| 135 var reflectable = |
| 136 js(backend.isAccessibleByReflection(member) ? '1' : '0'); |
| 137 defineStub('+$reflectionName', reflectable); |
| 138 } |
| 131 } | 139 } |
| 132 | 140 |
| 133 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, | 141 void addParameterStubs(FunctionElement member, |
| 134 [bool canTearOff = false]) { | 142 DefineStubFunction defineStub) { |
| 135 if (member.enclosingElement.isClosure()) { | |
| 136 ClosureClassElement cls = member.enclosingElement; | |
| 137 if (cls.supertype.element == compiler.boundClosureClass) { | |
| 138 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.'); | |
| 139 } | |
| 140 if (cls.methodElement.isInstanceMember()) { | |
| 141 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.'); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 // We fill the lists depending on the selector. For example, | 143 // We fill the lists depending on the selector. For example, |
| 146 // take method foo: | 144 // take method foo: |
| 147 // foo(a, b, {c, d}); | 145 // foo(a, b, {c, d}); |
| 148 // | 146 // |
| 149 // We may have multiple ways of calling foo: | 147 // We may have multiple ways of calling foo: |
| 150 // (1) foo(1, 2); | 148 // (1) foo(1, 2); |
| 151 // (2) foo(1, 2, c: 3); | 149 // (2) foo(1, 2, c: 3); |
| 152 // (3) foo(1, 2, d: 4); | 150 // (3) foo(1, 2, d: 4); |
| 153 // (4) foo(1, 2, c: 3, d: 4); | 151 // (4) foo(1, 2, c: 3, d: 4); |
| 154 // (5) foo(1, 2, d: 4, c: 3); | 152 // (5) foo(1, 2, d: 4, c: 3); |
| 155 // | 153 // |
| 156 // What we generate at the call sites are: | 154 // What we generate at the call sites are: |
| 157 // (1) foo$2(1, 2); | 155 // (1) foo$2(1, 2); |
| 158 // (2) foo$3$c(1, 2, 3); | 156 // (2) foo$3$c(1, 2, 3); |
| 159 // (3) foo$3$d(1, 2, 4); | 157 // (3) foo$3$d(1, 2, 4); |
| 160 // (4) foo$4$c$d(1, 2, 3, 4); | 158 // (4) foo$4$c$d(1, 2, 3, 4); |
| 161 // (5) foo$4$c$d(1, 2, 3, 4); | 159 // (5) foo$4$c$d(1, 2, 3, 4); |
| 162 // | 160 // |
| 163 // The stubs we generate are (expressed in Dart): | 161 // The stubs we generate are (expressed in Dart): |
| 164 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | 162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) |
| 165 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); | 163 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); |
| 166 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); | 164 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
| 167 // (4) No stub generated, call is direct. | 165 // (4) No stub generated, call is direct. |
| 168 // (5) No stub generated, call is direct. | 166 // (5) No stub generated, call is direct. |
| 169 | 167 |
| 170 Set<Selector> selectors = member.isInstanceMember() | 168 // Keep a cache of which stubs have already been generated, to |
| 171 ? compiler.codegenWorld.invokedNames[member.name] | 169 // avoid duplicates. Note that even if selectors are |
| 172 : null; // No stubs needed for static methods. | 170 // canonicalized, we would still need this cache: a typed selector |
| 173 | 171 // on A and a typed selector on B could yield the same stub. |
| 174 /// Returns all closure call selectors renamed to match this member. | 172 Set<String> generatedStubNames = new Set<String>(); |
| 175 Set<Selector> callSelectorsAsNamed() { | 173 bool isClosureInvocation = |
| 176 if (!canTearOff) return null; | 174 member.name == namer.closureInvocationSelectorName; |
| 177 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | 175 if (backend.isNeededForReflection(member) || |
| 178 namer.closureInvocationSelectorName]; | 176 (compiler.enabledFunctionApply && isClosureInvocation)) { |
| 179 if (callSelectors == null) return null; | 177 // If [Function.apply] is called, we pessimistically compile all |
| 180 return callSelectors.map((Selector callSelector) { | 178 // possible stubs for this closure. |
| 181 return new Selector.call( | 179 FunctionSignature signature = member.computeSignature(compiler); |
| 182 member.name, member.getLibrary(), | 180 Set<Selector> selectors = signature.optionalParametersAreNamed |
| 183 callSelector.argumentCount, callSelector.namedArguments); | 181 ? computeSeenNamedSelectors(member) |
| 184 }).toSet(); | 182 : computeOptionalSelectors(signature, member); |
| 185 } | 183 for (Selector selector in selectors) { |
| 186 if (selectors == null) { | 184 addParameterStub(member, selector, defineStub, generatedStubNames); |
| 187 selectors = callSelectorsAsNamed(); | 185 } |
| 186 if (signature.optionalParametersAreNamed && isClosureInvocation) { |
| 187 addCatchAllParameterStub(member, signature, defineStub); |
| 188 } |
| 189 } else { |
| 190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
| 188 if (selectors == null) return; | 191 if (selectors == null) return; |
| 189 } else { | |
| 190 Set<Selector> callSelectors = callSelectorsAsNamed(); | |
| 191 if (callSelectors != null) { | |
| 192 selectors = selectors.union(callSelectors); | |
| 193 } | |
| 194 } | |
| 195 Set<Selector> untypedSelectors = new Set<Selector>(); | |
| 196 if (selectors != null) { | |
| 197 for (Selector selector in selectors) { | 192 for (Selector selector in selectors) { |
| 198 if (!selector.appliesUnnamed(member, compiler)) continue; | 193 if (!selector.applies(member, compiler)) continue; |
| 199 if (untypedSelectors.add(selector.asUntyped)) { | 194 addParameterStub(member, selector, defineStub, generatedStubNames); |
| 200 // TODO(ahe): Is the last argument to [addParameterStub] needed? | 195 } |
| 201 addParameterStub(member, selector, defineStub, new Set<String>()); | 196 } |
| 202 } | 197 } |
| 203 } | 198 |
| 204 } | 199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) { |
| 205 if (canTearOff) { | 200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name]; |
| 206 selectors = compiler.codegenWorld.invokedNames[ | 201 Set<Selector> result = new Set<Selector>(); |
| 207 namer.closureInvocationSelectorName]; | 202 if (selectors == null) return result; |
| 208 if (selectors != null) { | 203 for (Selector selector in selectors) { |
| 209 for (Selector selector in selectors) { | 204 if (!selector.applies(element, compiler)) continue; |
| 210 selector = new Selector.call( | 205 result.add(selector); |
| 211 member.name, member.getLibrary(), | 206 } |
| 212 selector.argumentCount, selector.namedArguments); | 207 return result; |
| 213 if (!selector.appliesUnnamed(member, compiler)) continue; | 208 } |
| 214 if (untypedSelectors.add(selector)) { | 209 |
| 215 // TODO(ahe): Is the last argument to [addParameterStub] needed? | 210 void addCatchAllParameterStub(FunctionElement member, |
| 216 addParameterStub(member, selector, defineStub, new Set<String>()); | 211 FunctionSignature signature, |
| 217 } | 212 DefineStubFunction defineStub) { |
| 218 } | 213 // See Primities.applyFunction in js_helper.dart for details. |
| 219 } | 214 List<jsAst.Property> properties = <jsAst.Property>[]; |
| 220 } | 215 for (Element element in signature.orderedOptionalParameters) { |
| 216 String jsName = backend.namer.safeName(element.name); |
| 217 Constant value = compiler.constantHandler.initialVariableValues[element]; |
| 218 jsAst.Expression reference = null; |
| 219 if (value == null) { |
| 220 reference = new jsAst.LiteralNull(); |
| 221 } else { |
| 222 reference = task.constantReference(value); |
| 223 } |
| 224 properties.add(new jsAst.Property(js.string(jsName), reference)); |
| 225 } |
| 226 defineStub( |
| 227 backend.namer.callCatchAllName, |
| 228 js.fun([], js.return_(new jsAst.ObjectInitializer(properties)))); |
| 229 } |
| 230 |
| 231 /** |
| 232 * Compute the set of possible selectors in the presence of optional |
| 233 * non-named parameters. |
| 234 */ |
| 235 Set<Selector> computeOptionalSelectors(FunctionSignature signature, |
| 236 FunctionElement element) { |
| 237 Set<Selector> selectors = new Set<Selector>(); |
| 238 // Add the selector that does not have any optional argument. |
| 239 selectors.add(new Selector(SelectorKind.CALL, |
| 240 element.name, |
| 241 element.getLibrary(), |
| 242 signature.requiredParameterCount, |
| 243 <String>[])); |
| 244 |
| 245 // For each optional parameter, we increment the number of passed |
| 246 // argument. |
| 247 for (int i = 1; i <= signature.optionalParameterCount; i++) { |
| 248 selectors.add(new Selector(SelectorKind.CALL, |
| 249 element.name, |
| 250 element.getLibrary(), |
| 251 signature.requiredParameterCount + i, |
| 252 <String>[])); |
| 253 } |
| 254 return selectors; |
| 255 } |
| 256 |
| 257 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { |
| 258 task.addComment('Static function getters', task.mainBuffer); |
| 259 for (FunctionElement element in |
| 260 Elements.sortedByPosition(staticGetters.keys)) { |
| 261 Element closure = staticGetters[element]; |
| 262 CodeBuffer buffer = |
| 263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; |
| 264 String closureClass = namer.isolateAccess(closure); |
| 265 String name = namer.getStaticClosureName(element); |
| 266 |
| 267 String closureName = namer.getStaticClosureName(element); |
| 268 jsAst.Node assignment = js( |
| 269 'init.globalFunctions["$closureName"] =' |
| 270 ' ${namer.globalObjectFor(element)}.$name =' |
| 271 ' new $closureClass(#, "$closureName")', |
| 272 namer.elementAccess(element)); |
| 273 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 274 buffer.write('$N'); |
| 275 } |
| 276 } |
| 277 |
| 278 void emitStaticFunctionClosures() { |
| 279 Set<FunctionElement> functionsNeedingGetter = |
| 280 compiler.codegenWorld.staticFunctionsNeedingGetter; |
| 281 for (FunctionElement element in |
| 282 Elements.sortedByPosition(functionsNeedingGetter)) { |
| 283 String superName = namer.getNameOfClass(compiler.closureClass); |
| 284 int parameterCount = element.functionSignature.parameterCount; |
| 285 String name = 'Closure\$$parameterCount'; |
| 286 assert(task.instantiatedClasses.contains(compiler.closureClass)); |
| 287 |
| 288 ClassElement closureClassElement = new ClosureClassElement( |
| 289 null, name, compiler, element, |
| 290 element.getCompilationUnit()); |
| 291 // Now add the methods on the closure class. The instance method does not |
| 292 // have the correct name. Since [addParameterStubs] use the name to create |
| 293 // its stubs we simply create a fake element with the correct name. |
| 294 // Note: the callElement will not have any enclosingElement. |
| 295 FunctionElement callElement = |
| 296 new ClosureInvocationElement(namer.closureInvocationSelectorName, |
| 297 element); |
| 298 |
| 299 String invocationName = namer.instanceMethodName(callElement); |
| 300 String mangledName = namer.getNameOfClass(closureClassElement); |
| 301 |
| 302 // Define the constructor with a name so that Object.toString can |
| 303 // find the class name of the closure class. |
| 304 ClassBuilder closureBuilder = new ClassBuilder(); |
| 305 // If a static function is used as a closure we need to add its name |
| 306 // in case it is used in spawnFunction. |
| 307 String methodName = namer.STATIC_CLOSURE_NAME_NAME; |
| 308 List<String> fieldNames = <String>[invocationName, methodName]; |
| 309 closureBuilder.addProperty('', |
| 310 js.string("$superName;${fieldNames.join(',')}")); |
| 311 |
| 312 addParameterStubs(callElement, closureBuilder.addProperty); |
| 313 |
| 314 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| 315 RuntimeTypes rti = backend.rti; |
| 316 // [:() => null:] is dummy encoding of [this] which is never needed for |
| 317 // the encoding of the type of the static [method]. |
| 318 jsAst.Expression encoding = |
| 319 rti.getSignatureEncoding(methodType, js('null')); |
| 320 String operatorSignature = namer.operatorSignature(); |
| 321 // TODO(johnniwinther): Make MiniJsParser support function expressions. |
| 322 closureBuilder.addProperty(operatorSignature, encoding); |
| 323 } |
| 324 |
| 325 FunctionType methodType = element.computeType(compiler); |
| 326 Map<FunctionType, bool> functionTypeChecks = |
| 327 task.typeTestEmitter.getFunctionTypeChecksOn(methodType); |
| 328 task.typeTestEmitter.generateFunctionTypeTests( |
| 329 element, methodType, functionTypeChecks, |
| 330 emitFunctionTypeSignature); |
| 331 |
| 332 closureClassElement = |
| 333 addClosureIfNew(closureBuilder, closureClassElement, fieldNames); |
| 334 staticGetters[element] = closureClassElement; |
| 335 |
| 336 } |
| 337 } |
| 338 |
| 339 ClassElement addClosureIfNew(ClassBuilder builder, |
| 340 ClassElement closure, |
| 341 List<String> fieldNames) { |
| 342 String key = |
| 343 jsAst.prettyPrint(builder.toObjectInitializer(), compiler).getText(); |
| 344 return methodClosures.putIfAbsent(key, () { |
| 345 String mangledName = namer.getNameOfClass(closure); |
| 346 emitClosureInPrecompiledFunction(mangledName, fieldNames); |
| 347 return closure; |
| 348 }); |
| 349 } |
| 350 |
| 351 void emitClosureInPrecompiledFunction(String mangledName, |
| 352 List<String> fieldNames) { |
| 353 List<String> fields = fieldNames; |
| 354 String constructorName = mangledName; |
| 355 task.precompiledFunction.add(new jsAst.FunctionDeclaration( |
| 356 new jsAst.VariableDeclaration(constructorName), |
| 357 js.fun(fields, fields.map( |
| 358 (name) => js('this.$name = $name')).toList()))); |
| 359 task.precompiledFunction.addAll([ |
| 360 js('$constructorName.builtin\$cls = "$constructorName"'), |
| 361 js('\$desc=\$collectedClasses.$constructorName'), |
| 362 js.if_('\$desc instanceof Array', js('\$desc = \$desc[1]')), |
| 363 js('$constructorName.prototype = \$desc'), |
| 364 ]); |
| 365 |
| 366 task.precompiledConstructorNames.add(js(constructorName)); |
| 221 } | 367 } |
| 222 | 368 |
| 223 /** | 369 /** |
| 224 * Documentation wanted -- johnniwinther | 370 * Documentation wanted -- johnniwinther |
| 225 * | 371 * |
| 372 * Invariant: [member] must be a declaration element. |
| 373 */ |
| 374 void emitDynamicFunctionGetter(FunctionElement member, |
| 375 DefineStubFunction defineStub) { |
| 376 assert(invariant(member, member.isDeclaration)); |
| 377 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); |
| 378 // For every method that has the same name as a property-get we create a |
| 379 // getter that returns a bound closure. Say we have a class 'A' with method |
| 380 // 'foo' and somewhere in the code there is a dynamic property get of |
| 381 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
| 382 // |
| 383 // class A { |
| 384 // foo(x, y, z) { ... } // Original function. |
| 385 // get foo { return new BoundClosure499(this, "foo"); } |
| 386 // } |
| 387 // class BoundClosure499 extends BoundClosure { |
| 388 // BoundClosure499(this.self, this.name); |
| 389 // $call3(x, y, z) { return self[name](x, y, z); } |
| 390 // } |
| 391 |
| 392 bool hasOptionalParameters = member.optionalParameterCount(compiler) != 0; |
| 393 int parameterCount = member.parameterCount(compiler); |
| 394 |
| 395 // Intercepted methods take an extra parameter, which is the |
| 396 // receiver of the call. |
| 397 bool inInterceptor = backend.isInterceptedMethod(member); |
| 398 List<String> fieldNames = <String>[]; |
| 399 compiler.boundClosureClass.forEachInstanceField((_, Element field) { |
| 400 fieldNames.add(namer.instanceFieldPropertyName(field)); |
| 401 }); |
| 402 |
| 403 ClassElement classElement = member.getEnclosingClass(); |
| 404 String name = inInterceptor |
| 405 ? 'BoundClosure\$i${parameterCount}' |
| 406 : 'BoundClosure\$${parameterCount}'; |
| 407 |
| 408 ClassElement closureClassElement = new ClosureClassElement( |
| 409 null, name, compiler, member, |
| 410 member.getCompilationUnit()); |
| 411 String superName = namer.getNameOfClass(closureClassElement.superclass); |
| 412 |
| 413 // Define the constructor with a name so that Object.toString can |
| 414 // find the class name of the closure class. |
| 415 ClassBuilder boundClosureBuilder = new ClassBuilder(); |
| 416 boundClosureBuilder.addProperty('', |
| 417 js.string("$superName;${fieldNames.join(',')}")); |
| 418 // Now add the methods on the closure class. The instance method does not |
| 419 // have the correct name. Since [addParameterStubs] use the name to create |
| 420 // its stubs we simply create a fake element with the correct name. |
| 421 // Note: the callElement will not have any enclosingElement. |
| 422 FunctionElement callElement = new ClosureInvocationElement( |
| 423 namer.closureInvocationSelectorName, member); |
| 424 |
| 425 String invocationName = namer.instanceMethodName(callElement); |
| 426 |
| 427 List<String> parameters = <String>[]; |
| 428 List<jsAst.Expression> arguments = |
| 429 <jsAst.Expression>[js('this')[fieldNames[0]]]; |
| 430 if (inInterceptor) { |
| 431 arguments.add(js('this')[fieldNames[2]]); |
| 432 } |
| 433 for (int i = 0; i < parameterCount; i++) { |
| 434 String name = 'p$i'; |
| 435 parameters.add(name); |
| 436 arguments.add(js(name)); |
| 437 } |
| 438 |
| 439 jsAst.Expression fun = js.fun( |
| 440 parameters, |
| 441 js.return_( |
| 442 js('this')[fieldNames[1]]['call'](arguments))); |
| 443 boundClosureBuilder.addProperty(invocationName, fun); |
| 444 |
| 445 addParameterStubs(callElement, boundClosureBuilder.addProperty); |
| 446 |
| 447 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| 448 jsAst.Expression encoding = backend.rti.getSignatureEncoding( |
| 449 methodType, js('this')[fieldNames[0]]); |
| 450 String operatorSignature = namer.operatorSignature(); |
| 451 boundClosureBuilder.addProperty(operatorSignature, encoding); |
| 452 } |
| 453 |
| 454 DartType memberType = member.computeType(compiler); |
| 455 Map<FunctionType, bool> functionTypeChecks = |
| 456 task.typeTestEmitter.getFunctionTypeChecksOn(memberType); |
| 457 |
| 458 task.typeTestEmitter.generateFunctionTypeTests( |
| 459 member, memberType, functionTypeChecks, |
| 460 emitFunctionTypeSignature); |
| 461 |
| 462 closureClassElement = |
| 463 addClosureIfNew(boundClosureBuilder, closureClassElement, fieldNames); |
| 464 |
| 465 String closureClass = namer.isolateAccess(closureClassElement); |
| 466 |
| 467 // And finally the getter. |
| 468 String getterName = namer.getterName(member); |
| 469 String targetName = namer.instanceMethodName(member); |
| 470 |
| 471 parameters = <String>[]; |
| 472 jsAst.PropertyAccess method = |
| 473 backend.namer.elementAccess(classElement)['prototype'][targetName]; |
| 474 arguments = <jsAst.Expression>[js('this'), method]; |
| 475 |
| 476 if (inInterceptor) { |
| 477 String receiverArg = fieldNames[2]; |
| 478 parameters.add(receiverArg); |
| 479 arguments.add(js(receiverArg)); |
| 480 } else { |
| 481 // Put null in the intercepted receiver field. |
| 482 arguments.add(new jsAst.LiteralNull()); |
| 483 } |
| 484 |
| 485 arguments.add(js.string(targetName)); |
| 486 |
| 487 jsAst.Expression getterFunction = js.fun( |
| 488 parameters, js.return_(js(closureClass).newWith(arguments))); |
| 489 |
| 490 defineStub(getterName, getterFunction); |
| 491 } |
| 492 |
| 493 /** |
| 494 * Documentation wanted -- johnniwinther |
| 495 * |
| 226 * Invariant: [member] must be a declaration element. | 496 * Invariant: [member] must be a declaration element. |
| 227 */ | 497 */ |
| 228 void emitCallStubForGetter(Element member, | 498 void emitCallStubForGetter(Element member, |
| 229 Set<Selector> selectors, | 499 Set<Selector> selectors, |
| 230 AddPropertyFunction addProperty) { | 500 DefineStubFunction defineStub) { |
| 231 assert(invariant(member, member.isDeclaration)); | 501 assert(invariant(member, member.isDeclaration)); |
| 232 LibraryElement memberLibrary = member.getLibrary(); | 502 LibraryElement memberLibrary = member.getLibrary(); |
| 233 // If the method is intercepted, the stub gets the | 503 // If the method is intercepted, the stub gets the |
| 234 // receiver explicitely and we need to pass it to the getter call. | 504 // receiver explicitely and we need to pass it to the getter call. |
| 235 bool isInterceptedMethod = backend.isInterceptedMethod(member); | 505 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
| 236 | 506 |
| 237 const String receiverArgumentName = r'$receiver'; | 507 const String receiverArgumentName = r'$receiver'; |
| 238 | 508 |
| 239 jsAst.Expression buildGetter() { | 509 jsAst.Expression buildGetter() { |
| 240 if (member.isGetter()) { | 510 if (member.isGetter()) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 for (int i = 0; i < selector.argumentCount; i++) { | 542 for (int i = 0; i < selector.argumentCount; i++) { |
| 273 String name = 'arg$i'; | 543 String name = 'arg$i'; |
| 274 parameters.add(new jsAst.Parameter(name)); | 544 parameters.add(new jsAst.Parameter(name)); |
| 275 arguments.add(js(name)); | 545 arguments.add(js(name)); |
| 276 } | 546 } |
| 277 | 547 |
| 278 jsAst.Fun function = js.fun( | 548 jsAst.Fun function = js.fun( |
| 279 parameters, | 549 parameters, |
| 280 js.return_(buildGetter()[closureCallName](arguments))); | 550 js.return_(buildGetter()[closureCallName](arguments))); |
| 281 | 551 |
| 282 addProperty(invocationName, function); | 552 defineStub(invocationName, function); |
| 283 } | 553 } |
| 284 } | 554 } |
| 285 } | 555 } |
| 286 | 556 |
| 287 /** | 557 /** |
| 288 * Documentation wanted -- johnniwinther | 558 * Documentation wanted -- johnniwinther |
| 289 * | 559 * |
| 290 * Invariant: [member] must be a declaration element. | 560 * Invariant: [member] must be a declaration element. |
| 291 */ | 561 */ |
| 292 void emitExtraAccessors(Element member, ClassBuilder builder) { | 562 void emitExtraAccessors(Element member, ClassBuilder builder) { |
| 293 assert(invariant(member, member.isDeclaration)); | 563 assert(invariant(member, member.isDeclaration)); |
| 294 if (member.isGetter() || member.isField()) { | 564 if (member.isGetter() || member.isField()) { |
| 295 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 565 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
| 296 if (selectors != null && !selectors.isEmpty) { | 566 if (selectors != null && !selectors.isEmpty) { |
| 297 emitCallStubForGetter(member, selectors, builder.addProperty); | 567 emitCallStubForGetter(member, selectors, builder.addProperty); |
| 298 } | 568 } |
| 569 } else if (member.isFunction()) { |
| 570 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { |
| 571 emitDynamicFunctionGetter(member, builder.addProperty); |
| 572 } |
| 299 } | 573 } |
| 300 } | 574 } |
| 301 | 575 |
| 302 void addMember(Element member, ClassBuilder builder) { | 576 void addMember(Element member, ClassBuilder builder) { |
| 303 assert(invariant(member, member.isDeclaration)); | 577 assert(invariant(member, member.isDeclaration)); |
| 304 | 578 |
| 305 if (member.isField()) { | 579 if (member.isField()) { |
| 306 addMemberField(member, builder); | 580 addMemberField(member, builder); |
| 307 } else if (member.isFunction() || | 581 } else if (member.isFunction() || |
| 308 member.isGenerativeConstructorBody() || | 582 member.isGenerativeConstructorBody() || |
| 309 member.isGenerativeConstructor() || | 583 member.isGenerativeConstructor() || |
| 310 member.isAccessor()) { | 584 member.isAccessor()) { |
| 311 addMemberMethod(member, builder); | 585 addMemberMethod(member, builder); |
| 312 } else { | 586 } else { |
| 313 compiler.internalErrorOnElement( | 587 compiler.internalErrorOnElement( |
| 314 member, 'unexpected kind: "${member.kind}"'); | 588 member, 'unexpected kind: "${member.kind}"'); |
| 315 } | 589 } |
| 316 if (member.isInstanceMember()) emitExtraAccessors(member, builder); | 590 if (member.isInstanceMember()) emitExtraAccessors(member, builder); |
| 317 } | 591 } |
| 318 | 592 |
| 319 void addMemberMethod(FunctionElement member, ClassBuilder builder) { | 593 void addMemberMethod(FunctionElement member, ClassBuilder builder) { |
| 320 if (member.isAbstract) return; | 594 if (member.isAbstract) return; |
| 321 jsAst.Expression code = backend.generatedCode[member]; | 595 jsAst.Expression code = backend.generatedCode[member]; |
| 322 if (code == null) return; | 596 if (code == null) return; |
| 323 String name = namer.getNameOfMember(member); | 597 String name = namer.getNameOfMember(member); |
| 324 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); | 598 if (backend.isInterceptedMethod(member)) { |
| 325 FunctionSignature parameters = member.computeSignature(compiler); | 599 task.interceptorEmitter.interceptorInvocationNames.add(name); |
| 326 bool needsStubs = !parameters.optionalParameters.isEmpty; | 600 } |
| 327 bool canTearOff = false; | 601 code = task.metadataEmitter.extendWithMetadata(member, code); |
| 328 bool isClosure = false; | 602 builder.addProperty(name, code); |
| 329 String tearOffName; | 603 String reflectionName = task.getReflectionName(member, name); |
| 330 if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { | 604 if (reflectionName != null) { |
| 331 canTearOff = false; | 605 var reflectable = |
| 332 } else if (member.isInstanceMember()) { | 606 js(backend.isAccessibleByReflection(member) ? '1' : '0'); |
| 333 if (member.getEnclosingClass().isClosure()) { | 607 builder.addProperty('+$reflectionName', reflectable); |
| 334 canTearOff = false; | 608 jsAst.Node defaultValues = |
| 335 isClosure = true; | 609 task.metadataEmitter.reifyDefaultArguments(member); |
| 336 } else { | 610 if (defaultValues != null) { |
| 337 // Careful with operators. | 611 String unmangledName = member.name; |
| 338 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); | 612 builder.addProperty('*$unmangledName', defaultValues); |
| 339 tearOffName = namer.getterName(member); | |
| 340 } | 613 } |
| 341 } else { | |
| 342 canTearOff = | |
| 343 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); | |
| 344 tearOffName = namer.getStaticClosureName(member); | |
| 345 } | 614 } |
| 346 | 615 if (member.isInstanceMember()) { |
| 347 bool canBeReflected = backend.isAccessibleByReflection(member); | 616 // TODO(ahe): Where is this done for static/top-level methods? |
| 348 bool needStructuredInfo = | 617 FunctionSignature parameters = member.computeSignature(compiler); |
| 349 canTearOff || canBeReflected || compiler.enabledFunctionApply; | 618 if (!parameters.optionalParameters.isEmpty) { |
| 350 if (!needStructuredInfo) { | 619 addParameterStubs(member, builder.addProperty); |
| 351 builder.addProperty(name, code); | |
| 352 if (needsStubs) { | |
| 353 addParameterStubs( | |
| 354 member, | |
| 355 (Selector selector, jsAst.Fun function) { | |
| 356 builder.addProperty(namer.invocationName(selector), function); | |
| 357 }); | |
| 358 } | 620 } |
| 359 return; | |
| 360 } | 621 } |
| 361 | |
| 362 if (canTearOff) { | |
| 363 assert(invariant(member, !member.isGenerativeConstructor())); | |
| 364 assert(invariant(member, !member.isGenerativeConstructorBody())); | |
| 365 assert(invariant(member, !member.isConstructor())); | |
| 366 } | |
| 367 | |
| 368 // This element is needed for reflection or needs additional stubs. So we | |
| 369 // need to retain additional information. | |
| 370 | |
| 371 // The information is stored in an array with this format: | |
| 372 // | |
| 373 // 1. The JS function for this member. | |
| 374 // 2. First stub. | |
| 375 // 3. Name of first stub. | |
| 376 // ... | |
| 377 // M. Call name of this member. | |
| 378 // M+1. Call name of first stub. | |
| 379 // ... | |
| 380 // N. Getter name for tearOff. | |
| 381 // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). | |
| 382 // N+2. (Optional parameter count << 1) + | |
| 383 // (parameters.optionalParametersAreNamed ? 1 : 0). | |
| 384 // N+3. Index to function type in constant pool. | |
| 385 // N+4. First default argument. | |
| 386 // ... | |
| 387 // O. First parameter name (if needed for reflection or Function.apply). | |
| 388 // ... | |
| 389 // P. Unmangled name (if reflectable). | |
| 390 // P+1. First metadata (if reflectable). | |
| 391 // ... | |
| 392 | |
| 393 List expressions = []; | |
| 394 | |
| 395 String callSelectorString = 'null'; | |
| 396 if (member.isFunction()) { | |
| 397 Selector callSelector = | |
| 398 new Selector.fromElement(member, compiler).toCallSelector(); | |
| 399 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
| 400 } | |
| 401 | |
| 402 // On [requiredParameterCount], the lower bit is set if this method can be | |
| 403 // called reflectively. | |
| 404 int requiredParameterCount = parameters.requiredParameterCount << 1; | |
| 405 if (member.isAccessor()) requiredParameterCount++; | |
| 406 | |
| 407 int optionalParameterCount = parameters.optionalParameterCount << 1; | |
| 408 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | |
| 409 | |
| 410 expressions.add(code); | |
| 411 | |
| 412 // TODO(ahe): Remove comments from output. | |
| 413 List tearOffInfo = | |
| 414 [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')]; | |
| 415 | |
| 416 if (needsStubs || canTearOff) { | |
| 417 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
| 418 expressions.add(function); | |
| 419 if (member.isInstanceMember()) { | |
| 420 Set invokedSelectors = | |
| 421 compiler.codegenWorld.invokedNames[member.name]; | |
| 422 if (invokedSelectors != null && invokedSelectors.contains(selector)) { | |
| 423 expressions.add(js.string(namer.invocationName(selector))); | |
| 424 } else { | |
| 425 // Don't add a stub for calling this as a regular instance method, | |
| 426 // we only need the "call" stub for implicit closures of this | |
| 427 // method. | |
| 428 expressions.add("null"); | |
| 429 } | |
| 430 } else { | |
| 431 // Static methods don't need "named" stubs as the default arguments | |
| 432 // are inlined at call sites. But static methods might need "call" | |
| 433 // stubs for implicit closures. | |
| 434 expressions.add("null"); | |
| 435 // TOOD(ahe): Since we know when reading static data versus instance | |
| 436 // data, we can eliminate this element. | |
| 437 } | |
| 438 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
| 439 namer.closureInvocationSelectorName]; | |
| 440 Selector callSelector = selector.toCallSelector(); | |
| 441 String callSelectorString = 'null'; | |
| 442 if (canTearOff && callSelectors != null && | |
| 443 callSelectors.contains(callSelector)) { | |
| 444 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
| 445 } | |
| 446 tearOffInfo.add( | |
| 447 new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')); | |
| 448 }, canTearOff); | |
| 449 } | |
| 450 | |
| 451 jsAst.Expression memberTypeExpression; | |
| 452 if ((canTearOff || canBeReflected) && | |
| 453 !member.isGenerativeConstructorBody()) { | |
| 454 DartType memberType = member.computeType(compiler); | |
| 455 if (memberType.containsTypeVariables) { | |
| 456 jsAst.Expression thisAccess = js(r'this.$receiver'); | |
| 457 memberTypeExpression = | |
| 458 backend.rti.getSignatureEncoding(memberType, thisAccess); | |
| 459 } else { | |
| 460 memberTypeExpression = | |
| 461 js.toExpression(task.metadataEmitter.reifyType(memberType)); | |
| 462 } | |
| 463 } else { | |
| 464 memberTypeExpression = js('null'); | |
| 465 } | |
| 466 | |
| 467 expressions | |
| 468 ..addAll(tearOffInfo) | |
| 469 ..add((tearOffName == null || member.isAccessor()) | |
| 470 ? js("null") : js.string(tearOffName)) | |
| 471 ..add(requiredParameterCount) | |
| 472 ..add(optionalParameterCount) | |
| 473 ..add(memberTypeExpression) | |
| 474 ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); | |
| 475 | |
| 476 if (canBeReflected || compiler.enabledFunctionApply) { | |
| 477 parameters.orderedForEachParameter((Element parameter) { | |
| 478 expressions.add(task.metadataEmitter.reifyName(parameter.name)); | |
| 479 }); | |
| 480 } | |
| 481 if (canBeReflected) { | |
| 482 jsAst.LiteralString reflectionName; | |
| 483 if (member.isConstructor()) { | |
| 484 String reflectionNameString = task.getReflectionName(member, name); | |
| 485 reflectionName = | |
| 486 new jsAst.LiteralString( | |
| 487 '"new ${Elements.reconstructConstructorName(member)}"' | |
| 488 ' /* $reflectionNameString */'); | |
| 489 } else { | |
| 490 reflectionName = js.string(member.name); | |
| 491 } | |
| 492 expressions | |
| 493 ..add(reflectionName) | |
| 494 ..addAll(task.metadataEmitter.computeMetadata(member)); | |
| 495 } else if (isClosure && compiler.enabledFunctionApply) { | |
| 496 expressions.add(js.string(member.name)); | |
| 497 } | |
| 498 | |
| 499 builder.addProperty(name, js.toExpression(expressions)); | |
| 500 } | 622 } |
| 501 | 623 |
| 502 void addMemberField(VariableElement member, ClassBuilder builder) { | 624 void addMemberField(VariableElement member, ClassBuilder builder) { |
| 503 // For now, do nothing. | 625 // For now, do nothing. |
| 504 } | 626 } |
| 505 } | 627 } |
| OLD | NEW |