Chromium Code Reviews| 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 DefineStubFunction defineStub, | 28 AddStubFunction addStub, |
| 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 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 114 } | 114 } |
| 115 count++; | 115 count++; |
| 116 }); | 116 }); |
| 117 | 117 |
| 118 List body; | 118 List body; |
| 119 if (member.hasFixedBackendName()) { | 119 if (member.hasFixedBackendName()) { |
| 120 body = task.nativeEmitter.generateParameterStubStatements( | 120 body = task.nativeEmitter.generateParameterStubStatements( |
| 121 member, isInterceptedMethod, invocationName, | 121 member, isInterceptedMethod, invocationName, |
| 122 parametersBuffer, argumentsBuffer, | 122 parametersBuffer, argumentsBuffer, |
| 123 indexOfLastOptionalArgumentInParameters); | 123 indexOfLastOptionalArgumentInParameters); |
| 124 } else { | 124 } else if (member.isInstanceMember()) { |
| 125 body = [js.return_( | 125 body = [js.return_( |
| 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; | 126 js('this')[namer.getNameOfInstanceMember(member)](argumentsBuffer))]; |
| 127 } else { | |
| 128 body = [js.return_(namer.elementAccess(member)(argumentsBuffer))]; | |
| 127 } | 129 } |
| 128 | 130 |
| 129 jsAst.Fun function = js.fun(parametersBuffer, body); | 131 jsAst.Fun function = js.fun(parametersBuffer, body); |
| 130 | 132 |
| 131 defineStub(invocationName, function); | 133 addStub(selector, 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 } | |
| 139 } | 134 } |
| 140 | 135 |
| 141 void addParameterStubs(FunctionElement member, | 136 void addParameterStubs(FunctionElement member, AddStubFunction defineStub, |
| 142 DefineStubFunction defineStub) { | 137 [bool canTearOff = false]) { |
| 138 if (member.enclosingElement.isClosure()) { | |
| 139 ClosureClassElement cls = member.enclosingElement; | |
| 140 if (cls.supertype.element == compiler.boundClosureClass) { | |
| 141 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure1.'); | |
| 142 } | |
| 143 if (cls.methodElement.isInstanceMember()) { | |
| 144 compiler.internalErrorOnElement(cls.methodElement, 'Bound closure2.'); | |
| 145 } | |
| 146 } | |
| 147 | |
| 143 // We fill the lists depending on the selector. For example, | 148 // We fill the lists depending on the selector. For example, |
| 144 // take method foo: | 149 // take method foo: |
| 145 // foo(a, b, {c, d}); | 150 // foo(a, b, {c, d}); |
| 146 // | 151 // |
| 147 // We may have multiple ways of calling foo: | 152 // We may have multiple ways of calling foo: |
| 148 // (1) foo(1, 2); | 153 // (1) foo(1, 2); |
| 149 // (2) foo(1, 2, c: 3); | 154 // (2) foo(1, 2, c: 3); |
| 150 // (3) foo(1, 2, d: 4); | 155 // (3) foo(1, 2, d: 4); |
| 151 // (4) foo(1, 2, c: 3, d: 4); | 156 // (4) foo(1, 2, c: 3, d: 4); |
| 152 // (5) foo(1, 2, d: 4, c: 3); | 157 // (5) foo(1, 2, d: 4, c: 3); |
| 153 // | 158 // |
| 154 // What we generate at the call sites are: | 159 // What we generate at the call sites are: |
| 155 // (1) foo$2(1, 2); | 160 // (1) foo$2(1, 2); |
| 156 // (2) foo$3$c(1, 2, 3); | 161 // (2) foo$3$c(1, 2, 3); |
| 157 // (3) foo$3$d(1, 2, 4); | 162 // (3) foo$3$d(1, 2, 4); |
| 158 // (4) foo$4$c$d(1, 2, 3, 4); | 163 // (4) foo$4$c$d(1, 2, 3, 4); |
| 159 // (5) foo$4$c$d(1, 2, 3, 4); | 164 // (5) foo$4$c$d(1, 2, 3, 4); |
| 160 // | 165 // |
| 161 // The stubs we generate are (expressed in Dart): | 166 // The stubs we generate are (expressed in Dart): |
| 162 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) | 167 // (1) foo$2(a, b) => foo$4$c$d(a, b, null, null) |
| 163 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); | 168 // (2) foo$3$c(a, b, c) => foo$4$c$d(a, b, c, null); |
| 164 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); | 169 // (3) foo$3$d(a, b, d) => foo$4$c$d(a, b, null, d); |
| 165 // (4) No stub generated, call is direct. | 170 // (4) No stub generated, call is direct. |
| 166 // (5) No stub generated, call is direct. | 171 // (5) No stub generated, call is direct. |
| 167 | 172 |
| 168 // Keep a cache of which stubs have already been generated, to | 173 // Keep a cache of which stubs have already been generated, to |
| 169 // avoid duplicates. Note that even if selectors are | 174 // avoid duplicates. Note that even if selectors are |
| 170 // canonicalized, we would still need this cache: a typed selector | 175 // canonicalized, we would still need this cache: a typed selector |
| 171 // on A and a typed selector on B could yield the same stub. | 176 // on A and a typed selector on B could yield the same stub. |
| 172 Set<String> generatedStubNames = new Set<String>(); | 177 Set<Selector> selectors = null; |
|
kasperl
2013/11/29 10:10:55
Maybe use ?: so the comment about not needing stub
ahe
2013/12/06 15:57:53
Done.
| |
| 173 bool isClosureInvocation = | 178 if (member.isInstanceMember()) { |
| 174 member.name == namer.closureInvocationSelectorName; | 179 // No stubs needed for static methods. |
| 175 if (backend.isNeededForReflection(member) || | 180 selectors = compiler.codegenWorld.invokedNames[member.name]; |
| 176 (compiler.enabledFunctionApply && isClosureInvocation)) { | 181 } |
| 177 // If [Function.apply] is called, we pessimistically compile all | 182 |
| 178 // possible stubs for this closure. | 183 /// Returns all closure call selectors renamed to match this member. |
| 179 FunctionSignature signature = member.computeSignature(compiler); | 184 Set<Selector> callSelectorsAsNamed() { |
| 180 Set<Selector> selectors = signature.optionalParametersAreNamed | 185 if (!canTearOff) return null; |
| 181 ? computeSeenNamedSelectors(member) | 186 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ |
| 182 : computeOptionalSelectors(signature, member); | 187 namer.closureInvocationSelectorName]; |
| 188 if (callSelectors == null) return null; | |
| 189 return callSelectors.map((Selector callSelector) { | |
| 190 return new Selector.call( | |
| 191 member.name, member.getLibrary(), | |
| 192 callSelector.argumentCount, callSelector.namedArguments); | |
| 193 }).toSet(); | |
| 194 } | |
| 195 if (selectors == null) { | |
| 196 selectors = callSelectorsAsNamed(); | |
| 197 if (selectors == null) return; | |
| 198 } else { | |
| 199 Set<Selector> callSelectors = callSelectorsAsNamed(); | |
| 200 if (callSelectors != null) { | |
| 201 selectors = selectors.union(callSelectors); | |
| 202 } | |
| 203 } | |
| 204 Set<Selector> untypedSelectors = new Set(); | |
| 205 if (selectors != null) { | |
| 183 for (Selector selector in selectors) { | 206 for (Selector selector in selectors) { |
| 184 addParameterStub(member, selector, defineStub, generatedStubNames); | 207 if (!selector.appliesUnnamed(member, compiler)) continue; |
| 208 if (untypedSelectors.add(selector.asUntyped)) { | |
| 209 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
| 210 addParameterStub(member, selector, defineStub, new Set<String>()); | |
| 211 } | |
| 185 } | 212 } |
| 186 if (signature.optionalParametersAreNamed && isClosureInvocation) { | 213 } |
| 187 addCatchAllParameterStub(member, signature, defineStub); | 214 if (canTearOff) { |
| 188 } | 215 selectors = compiler.codegenWorld.invokedNames[ |
| 189 } else { | 216 namer.closureInvocationSelectorName]; |
| 190 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 217 if (selectors != null) { |
| 191 if (selectors == null) return; | 218 for (Selector selector in selectors) { |
| 192 for (Selector selector in selectors) { | 219 selector = new Selector.call( |
| 193 if (!selector.applies(member, compiler)) continue; | 220 member.name, member.getLibrary(), |
| 194 addParameterStub(member, selector, defineStub, generatedStubNames); | 221 selector.argumentCount, selector.namedArguments); |
| 222 if (!selector.appliesUnnamed(member, compiler)) continue; | |
| 223 if (untypedSelectors.add(selector)) { | |
| 224 // TODO(ahe): Is the last argument to [addParameterStub] needed? | |
| 225 addParameterStub(member, selector, defineStub, new Set<String>()); | |
| 226 } | |
| 227 } | |
| 195 } | 228 } |
| 196 } | 229 } |
| 197 } | 230 } |
| 198 | 231 |
| 199 Set<Selector> computeSeenNamedSelectors(FunctionElement element) { | |
| 200 Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name]; | |
| 201 Set<Selector> result = new Set<Selector>(); | |
| 202 if (selectors == null) return result; | |
| 203 for (Selector selector in selectors) { | |
| 204 if (!selector.applies(element, compiler)) continue; | |
| 205 result.add(selector); | |
| 206 } | |
| 207 return result; | |
| 208 } | |
| 209 | |
| 210 void addCatchAllParameterStub(FunctionElement member, | |
| 211 FunctionSignature signature, | |
| 212 DefineStubFunction defineStub) { | |
| 213 // See Primities.applyFunction in js_helper.dart for details. | |
| 214 List<jsAst.Property> properties = <jsAst.Property>[]; | |
| 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) { | 232 void emitStaticFunctionGetters(CodeBuffer eagerBuffer) { |
| 233 return; | |
| 258 task.addComment('Static function getters', task.mainBuffer); | 234 task.addComment('Static function getters', task.mainBuffer); |
| 259 for (FunctionElement element in | 235 for (FunctionElement element in |
| 260 Elements.sortedByPosition(staticGetters.keys)) { | 236 Elements.sortedByPosition(staticGetters.keys)) { |
| 261 Element closure = staticGetters[element]; | 237 Element closure = staticGetters[element]; |
| 262 CodeBuffer buffer = | 238 CodeBuffer buffer = |
| 263 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; | 239 task.isDeferred(element) ? task.deferredConstants : eagerBuffer; |
| 264 String closureClass = namer.isolateAccess(closure); | 240 String closureClass = namer.isolateAccess(closure); |
| 265 String name = namer.getStaticClosureName(element); | 241 String name = namer.getStaticClosureName(element); |
| 266 | 242 |
| 267 String closureName = namer.getStaticClosureName(element); | 243 String closureName = namer.getStaticClosureName(element); |
| 268 jsAst.Node assignment = js( | 244 jsAst.Node assignment = js( |
| 269 'init.globalFunctions["$closureName"] =' | 245 'init.globalFunctions["$closureName"] =' |
| 270 ' ${namer.globalObjectFor(element)}.$name =' | 246 ' ${namer.globalObjectFor(element)}.$name =' |
| 271 ' new $closureClass(#, "$closureName")', | 247 ' new $closureClass(#, "$closureName")', |
| 272 namer.elementAccess(element)); | 248 namer.elementAccess(element)); |
| 273 buffer.write(jsAst.prettyPrint(assignment, compiler)); | 249 buffer.write(jsAst.prettyPrint(assignment, compiler)); |
| 274 buffer.write('$N'); | 250 buffer.write('$N'); |
| 275 } | 251 } |
| 276 } | 252 } |
| 277 | 253 |
| 278 void emitStaticFunctionClosures() { | 254 void emitStaticFunctionClosures() { |
| 255 return; | |
| 279 Set<FunctionElement> functionsNeedingGetter = | 256 Set<FunctionElement> functionsNeedingGetter = |
| 280 compiler.codegenWorld.staticFunctionsNeedingGetter; | 257 compiler.codegenWorld.staticFunctionsNeedingGetter; |
| 281 for (FunctionElement element in | 258 for (FunctionElement element in |
| 282 Elements.sortedByPosition(functionsNeedingGetter)) { | 259 Elements.sortedByPosition(functionsNeedingGetter)) { |
| 283 String superName = namer.getNameOfClass(compiler.closureClass); | 260 String superName = namer.getNameOfClass(compiler.closureClass); |
| 284 int parameterCount = element.functionSignature.parameterCount; | 261 int parameterCount = element.functionSignature.parameterCount; |
| 285 String name = 'Closure\$$parameterCount'; | 262 String name = 'Closure\$$parameterCount'; |
| 286 assert(task.instantiatedClasses.contains(compiler.closureClass)); | 263 assert(task.instantiatedClasses.contains(compiler.closureClass)); |
| 287 | 264 |
| 288 ClassElement closureClassElement = new ClosureClassElement( | 265 ClassElement closureClassElement = new ClosureClassElement( |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 302 // Define the constructor with a name so that Object.toString can | 279 // Define the constructor with a name so that Object.toString can |
| 303 // find the class name of the closure class. | 280 // find the class name of the closure class. |
| 304 ClassBuilder closureBuilder = new ClassBuilder(); | 281 ClassBuilder closureBuilder = new ClassBuilder(); |
| 305 // If a static function is used as a closure we need to add its name | 282 // If a static function is used as a closure we need to add its name |
| 306 // in case it is used in spawnFunction. | 283 // in case it is used in spawnFunction. |
| 307 String methodName = namer.STATIC_CLOSURE_NAME_NAME; | 284 String methodName = namer.STATIC_CLOSURE_NAME_NAME; |
| 308 List<String> fieldNames = <String>[invocationName, methodName]; | 285 List<String> fieldNames = <String>[invocationName, methodName]; |
| 309 closureBuilder.addProperty('', | 286 closureBuilder.addProperty('', |
| 310 js.string("$superName;${fieldNames.join(',')}")); | 287 js.string("$superName;${fieldNames.join(',')}")); |
| 311 | 288 |
| 312 addParameterStubs(callElement, closureBuilder.addProperty); | 289 addParameterStubs( |
| 290 callElement, | |
| 291 (Selector selector, jsAst.Fun function) { | |
| 292 closureBuilder.addProperty( | |
| 293 namer.invocationName(selector), function); | |
| 294 }); | |
| 313 | 295 |
| 314 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | 296 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| 315 RuntimeTypes rti = backend.rti; | 297 RuntimeTypes rti = backend.rti; |
| 316 // [:() => null:] is dummy encoding of [this] which is never needed for | 298 // [:() => null:] is dummy encoding of [this] which is never needed for |
| 317 // the encoding of the type of the static [method]. | 299 // the encoding of the type of the static [method]. |
| 318 jsAst.Expression encoding = | 300 jsAst.Expression encoding = |
| 319 rti.getSignatureEncoding(methodType, js('null')); | 301 rti.getSignatureEncoding(methodType, js('null')); |
| 320 String operatorSignature = namer.operatorSignature(); | 302 String operatorSignature = namer.operatorSignature(); |
| 321 // TODO(johnniwinther): Make MiniJsParser support function expressions. | 303 // TODO(johnniwinther): Make MiniJsParser support function expressions. |
| 322 closureBuilder.addProperty(operatorSignature, encoding); | 304 closureBuilder.addProperty(operatorSignature, encoding); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 | 347 |
| 366 task.precompiledConstructorNames.add(js(constructorName)); | 348 task.precompiledConstructorNames.add(js(constructorName)); |
| 367 } | 349 } |
| 368 | 350 |
| 369 /** | 351 /** |
| 370 * Documentation wanted -- johnniwinther | 352 * Documentation wanted -- johnniwinther |
| 371 * | 353 * |
| 372 * Invariant: [member] must be a declaration element. | 354 * Invariant: [member] must be a declaration element. |
| 373 */ | 355 */ |
| 374 void emitDynamicFunctionGetter(FunctionElement member, | 356 void emitDynamicFunctionGetter(FunctionElement member, |
| 375 DefineStubFunction defineStub) { | 357 AddPropertyFunction addProperty) { |
| 376 assert(invariant(member, member.isDeclaration)); | 358 assert(invariant(member, member.isDeclaration)); |
| 377 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); | 359 assert(task.instantiatedClasses.contains(compiler.boundClosureClass)); |
| 378 // For every method that has the same name as a property-get we create a | 360 // 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 | 361 // 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 | 362 // '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): | 363 // 'foo'. Then we generate the following code (in pseudo Dart/JavaScript): |
| 382 // | 364 // |
| 383 // class A { | 365 // class A { |
| 384 // foo(x, y, z) { ... } // Original function. | 366 // foo(x, y, z) { ... } // Original function. |
| 385 // get foo { return new BoundClosure499(this, "foo"); } | 367 // get foo { return new BoundClosure499(this, "foo"); } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 parameters.add(name); | 417 parameters.add(name); |
| 436 arguments.add(js(name)); | 418 arguments.add(js(name)); |
| 437 } | 419 } |
| 438 | 420 |
| 439 jsAst.Expression fun = js.fun( | 421 jsAst.Expression fun = js.fun( |
| 440 parameters, | 422 parameters, |
| 441 js.return_( | 423 js.return_( |
| 442 js('this')[fieldNames[1]]['call'](arguments))); | 424 js('this')[fieldNames[1]]['call'](arguments))); |
| 443 boundClosureBuilder.addProperty(invocationName, fun); | 425 boundClosureBuilder.addProperty(invocationName, fun); |
| 444 | 426 |
| 445 addParameterStubs(callElement, boundClosureBuilder.addProperty); | 427 addParameterStubs( |
| 428 callElement, | |
| 429 (Selector selector, jsAst.Fun function) { | |
| 430 boundClosureBuilder.addProperty( | |
| 431 namer.invocationName(selector), function); | |
| 432 }); | |
| 446 | 433 |
| 447 void emitFunctionTypeSignature(Element method, FunctionType methodType) { | 434 void emitFunctionTypeSignature(Element method, FunctionType methodType) { |
| 448 jsAst.Expression encoding = backend.rti.getSignatureEncoding( | 435 jsAst.Expression encoding = backend.rti.getSignatureEncoding( |
| 449 methodType, js('this')[fieldNames[0]]); | 436 methodType, js('this')[fieldNames[0]]); |
| 450 String operatorSignature = namer.operatorSignature(); | 437 String operatorSignature = namer.operatorSignature(); |
| 451 boundClosureBuilder.addProperty(operatorSignature, encoding); | 438 boundClosureBuilder.addProperty(operatorSignature, encoding); |
| 452 } | 439 } |
| 453 | 440 |
| 454 DartType memberType = member.computeType(compiler); | 441 DartType memberType = member.computeType(compiler); |
| 455 Map<FunctionType, bool> functionTypeChecks = | 442 Map<FunctionType, bool> functionTypeChecks = |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 480 } else { | 467 } else { |
| 481 // Put null in the intercepted receiver field. | 468 // Put null in the intercepted receiver field. |
| 482 arguments.add(new jsAst.LiteralNull()); | 469 arguments.add(new jsAst.LiteralNull()); |
| 483 } | 470 } |
| 484 | 471 |
| 485 arguments.add(js.string(targetName)); | 472 arguments.add(js.string(targetName)); |
| 486 | 473 |
| 487 jsAst.Expression getterFunction = js.fun( | 474 jsAst.Expression getterFunction = js.fun( |
| 488 parameters, js.return_(js(closureClass).newWith(arguments))); | 475 parameters, js.return_(js(closureClass).newWith(arguments))); |
| 489 | 476 |
| 490 defineStub(getterName, getterFunction); | 477 addProperty(getterName, getterFunction); |
| 491 } | 478 } |
| 492 | 479 |
| 493 /** | 480 /** |
| 494 * Documentation wanted -- johnniwinther | 481 * Documentation wanted -- johnniwinther |
| 495 * | 482 * |
| 496 * Invariant: [member] must be a declaration element. | 483 * Invariant: [member] must be a declaration element. |
| 497 */ | 484 */ |
| 498 void emitCallStubForGetter(Element member, | 485 void emitCallStubForGetter(Element member, |
| 499 Set<Selector> selectors, | 486 Set<Selector> selectors, |
| 500 DefineStubFunction defineStub) { | 487 AddPropertyFunction addProperty) { |
| 501 assert(invariant(member, member.isDeclaration)); | 488 assert(invariant(member, member.isDeclaration)); |
| 502 LibraryElement memberLibrary = member.getLibrary(); | 489 LibraryElement memberLibrary = member.getLibrary(); |
| 503 // If the method is intercepted, the stub gets the | 490 // If the method is intercepted, the stub gets the |
| 504 // receiver explicitely and we need to pass it to the getter call. | 491 // receiver explicitely and we need to pass it to the getter call. |
| 505 bool isInterceptedMethod = backend.isInterceptedMethod(member); | 492 bool isInterceptedMethod = backend.isInterceptedMethod(member); |
| 506 | 493 |
| 507 const String receiverArgumentName = r'$receiver'; | 494 const String receiverArgumentName = r'$receiver'; |
| 508 | 495 |
| 509 jsAst.Expression buildGetter() { | 496 jsAst.Expression buildGetter() { |
| 510 if (member.isGetter()) { | 497 if (member.isGetter()) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 542 for (int i = 0; i < selector.argumentCount; i++) { | 529 for (int i = 0; i < selector.argumentCount; i++) { |
| 543 String name = 'arg$i'; | 530 String name = 'arg$i'; |
| 544 parameters.add(new jsAst.Parameter(name)); | 531 parameters.add(new jsAst.Parameter(name)); |
| 545 arguments.add(js(name)); | 532 arguments.add(js(name)); |
| 546 } | 533 } |
| 547 | 534 |
| 548 jsAst.Fun function = js.fun( | 535 jsAst.Fun function = js.fun( |
| 549 parameters, | 536 parameters, |
| 550 js.return_(buildGetter()[closureCallName](arguments))); | 537 js.return_(buildGetter()[closureCallName](arguments))); |
| 551 | 538 |
| 552 defineStub(invocationName, function); | 539 addProperty(invocationName, function); |
| 553 } | 540 } |
| 554 } | 541 } |
| 555 } | 542 } |
| 556 | 543 |
| 557 /** | 544 /** |
| 558 * Documentation wanted -- johnniwinther | 545 * Documentation wanted -- johnniwinther |
| 559 * | 546 * |
| 560 * Invariant: [member] must be a declaration element. | 547 * Invariant: [member] must be a declaration element. |
| 561 */ | 548 */ |
| 562 void emitExtraAccessors(Element member, ClassBuilder builder) { | 549 void emitExtraAccessors(Element member, ClassBuilder builder) { |
| 563 assert(invariant(member, member.isDeclaration)); | 550 assert(invariant(member, member.isDeclaration)); |
| 564 if (member.isGetter() || member.isField()) { | 551 if (member.isGetter() || member.isField()) { |
| 565 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; | 552 Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name]; |
| 566 if (selectors != null && !selectors.isEmpty) { | 553 if (selectors != null && !selectors.isEmpty) { |
| 567 emitCallStubForGetter(member, selectors, builder.addProperty); | 554 emitCallStubForGetter(member, selectors, builder.addProperty); |
| 568 } | 555 } |
| 569 } else if (member.isFunction()) { | |
| 570 if (compiler.codegenWorld.hasInvokedGetter(member, compiler)) { | |
| 571 emitDynamicFunctionGetter(member, builder.addProperty); | |
| 572 } | |
| 573 } | 556 } |
| 574 } | 557 } |
| 575 | 558 |
| 576 void addMember(Element member, ClassBuilder builder) { | 559 void addMember(Element member, ClassBuilder builder) { |
| 577 assert(invariant(member, member.isDeclaration)); | 560 assert(invariant(member, member.isDeclaration)); |
| 578 | 561 |
| 579 if (member.isField()) { | 562 if (member.isField()) { |
| 580 addMemberField(member, builder); | 563 addMemberField(member, builder); |
| 581 } else if (member.isFunction() || | 564 } else if (member.isFunction() || |
| 582 member.isGenerativeConstructorBody() || | 565 member.isGenerativeConstructorBody() || |
| 583 member.isGenerativeConstructor() || | 566 member.isGenerativeConstructor() || |
| 584 member.isAccessor()) { | 567 member.isAccessor()) { |
| 585 addMemberMethod(member, builder); | 568 addMemberMethod(member, builder); |
| 586 } else { | 569 } else { |
| 587 compiler.internalErrorOnElement( | 570 compiler.internalErrorOnElement( |
| 588 member, 'unexpected kind: "${member.kind}"'); | 571 member, 'unexpected kind: "${member.kind}"'); |
| 589 } | 572 } |
| 590 if (member.isInstanceMember()) emitExtraAccessors(member, builder); | 573 if (member.isInstanceMember()) emitExtraAccessors(member, builder); |
| 591 } | 574 } |
| 592 | 575 |
| 593 void addMemberMethod(FunctionElement member, ClassBuilder builder) { | 576 void addMemberMethod(FunctionElement member, ClassBuilder builder) { |
| 594 if (member.isAbstract(compiler)) return; | 577 if (member.isAbstract(compiler)) return; |
| 595 jsAst.Expression code = backend.generatedCode[member]; | 578 jsAst.Expression code = backend.generatedCode[member]; |
| 596 if (code == null) return; | 579 if (code == null) return; |
| 597 String name = namer.getNameOfMember(member); | 580 String name = namer.getNameOfMember(member); |
| 598 if (backend.isInterceptedMethod(member)) { | 581 task.interceptorEmitter.recordMangledNameOfMemberMethod(member, name); |
| 599 task.interceptorEmitter.interceptorInvocationNames.add(name); | 582 FunctionSignature parameters = member.computeSignature(compiler); |
| 583 bool needsStubs = !parameters.optionalParameters.isEmpty; | |
| 584 bool canTearOff = false; | |
| 585 bool isClosure = false; | |
| 586 String tearOffName; | |
| 587 if (!member.isFunction() || member.isConstructor() || member.isAccessor()) { | |
| 588 canTearOff = false; | |
| 589 } else if (member.isInstanceMember()) { | |
| 590 if (member.getEnclosingClass().isClosure()) { | |
| 591 canTearOff = false; | |
| 592 isClosure = true; | |
| 593 } else { | |
| 594 // Careful with operators. | |
| 595 canTearOff = compiler.codegenWorld.hasInvokedGetter(member, compiler); | |
| 596 tearOffName = namer.getterName(member); | |
| 597 } | |
| 598 } else { | |
| 599 canTearOff = | |
| 600 compiler.codegenWorld.staticFunctionsNeedingGetter.contains(member); | |
| 601 tearOffName = namer.getStaticClosureName(member); | |
| 600 } | 602 } |
| 601 code = task.metadataEmitter.extendWithMetadata(member, code); | 603 |
| 602 builder.addProperty(name, code); | 604 bool canBeReflected = backend.isAccessibleByReflection(member); |
| 603 String reflectionName = task.getReflectionName(member, name); | 605 bool needStructuredInfo = canTearOff || canBeReflected || compiler.enabledFu nctionApply; |
|
kasperl
2013/11/29 10:10:55
Long line.
ahe
2013/12/06 15:57:53
Done.
| |
| 604 if (reflectionName != null) { | 606 if (!needStructuredInfo) { |
| 605 var reflectable = | 607 builder.addProperty(name, code); |
| 606 js(backend.isAccessibleByReflection(member) ? '1' : '0'); | 608 if (needsStubs) { |
| 607 builder.addProperty('+$reflectionName', reflectable); | 609 addParameterStubs( |
| 608 jsAst.Node defaultValues = | 610 member, |
| 609 task.metadataEmitter.reifyDefaultArguments(member); | 611 (Selector selector, jsAst.Fun function) { |
| 610 if (defaultValues != null) { | 612 builder.addProperty(namer.invocationName(selector), function); |
| 611 String unmangledName = member.name; | 613 }); |
| 612 builder.addProperty('*$unmangledName', defaultValues); | |
| 613 } | 614 } |
| 615 return; | |
| 614 } | 616 } |
| 615 if (member.isInstanceMember()) { | 617 |
| 616 // TODO(ahe): Where is this done for static/top-level methods? | 618 if (canTearOff) { |
| 617 FunctionSignature parameters = member.computeSignature(compiler); | 619 assert(invariant(member, !member.isGenerativeConstructor())); |
| 618 if (!parameters.optionalParameters.isEmpty) { | 620 assert(invariant(member, !member.isGenerativeConstructorBody())); |
| 619 addParameterStubs(member, builder.addProperty); | 621 assert(invariant(member, !member.isConstructor())); |
| 622 } | |
| 623 | |
| 624 // This element is needed for reflection or needs additional stubs. So we | |
| 625 // need to retain additional information. | |
| 626 | |
| 627 // The information is stored in an array with this format: | |
| 628 // | |
| 629 // 1. The JS function for this member. | |
| 630 // 2. First stub. | |
| 631 // 3. Name of first stub. | |
| 632 // ... | |
| 633 // M. Call name of this member. | |
| 634 // M+1. Call name of first stub. | |
| 635 // ... | |
| 636 // N. Getter name for tearOff. | |
| 637 // N+1. (Required parameter count << 1) + (member.isAccessor() ? 1 : 0). | |
| 638 // N+2. (Optional parameter count << 1) + | |
| 639 // (parameters.optionalParametersAreNamed ? 1 : 0). | |
| 640 // N+3. Index to function type in constant pool. | |
| 641 // N+4. First default argument. | |
| 642 // ... | |
| 643 // O. First parameter name (if needed for reflection or Function.apply). | |
| 644 // ... | |
| 645 // P. Unmangled name (if reflectable). | |
| 646 // P+1. First metadata (if reflectable). | |
| 647 // ... | |
| 648 | |
| 649 List expressions = []; | |
| 650 | |
| 651 Selector callSelector; | |
| 652 if (member.isFunction()) { | |
| 653 callSelector = | |
| 654 new Selector.fromElement(member, compiler).toCallSelector(); | |
| 655 } | |
| 656 | |
| 657 // On [requiredParameterCount], the lower bit is set if this method can be | |
| 658 // called reflectively. | |
| 659 int requiredParameterCount = parameters.requiredParameterCount << 1; | |
| 660 if (member.isAccessor()) requiredParameterCount++; | |
| 661 | |
| 662 int optionalParameterCount = parameters.optionalParameterCount << 1; | |
| 663 if (parameters.optionalParametersAreNamed) optionalParameterCount++; | |
| 664 | |
| 665 expressions.add(code); | |
| 666 | |
| 667 String callSelectorString = | |
| 668 callSelector == null ? null : '"${namer.invocationName(callSelector)}"'; | |
| 669 List tearOffInfo = | |
| 670 [new jsAst.LiteralString('$callSelectorString /* tearOffInfo */')]; | |
| 671 | |
| 672 if (needsStubs || canTearOff) { | |
| 673 addParameterStubs(member, (Selector selector, jsAst.Fun function) { | |
| 674 expressions.add(function); | |
| 675 if (member.isInstanceMember()) { | |
| 676 Set invokedSelectors = | |
| 677 compiler.codegenWorld.invokedNames[member.name]; | |
| 678 if (invokedSelectors != null && invokedSelectors.contains(selector)) { | |
| 679 expressions.add(js.string(namer.invocationName(selector))); | |
| 680 } else { | |
| 681 // Don't add a stub for calling this as a regular instance method, | |
| 682 // we only need the "call" stub for implicit closures of this | |
| 683 // method. | |
| 684 expressions.add("null"); | |
| 685 } | |
| 686 } else { | |
| 687 // Static methods don't need "named" stubs as the default arguments | |
| 688 // are inlined at call sites. But static methods might need "call" | |
| 689 // stubs for implicit closures. | |
| 690 expressions.add("null"); | |
| 691 // TOOD(ahe): Since we know when reading static data versus instance | |
| 692 // data, we can eliminate this element. | |
| 693 } | |
| 694 Set<Selector> callSelectors = compiler.codegenWorld.invokedNames[ | |
| 695 namer.closureInvocationSelectorName]; | |
| 696 Selector callSelector = selector.toCallSelector(); | |
| 697 String callSelectorString = 'null'; | |
| 698 if (canTearOff && callSelectors != null && callSelectors.contains(callSe lector)) { | |
|
kasperl
2013/11/29 10:10:55
Long lines (there are a number of occurrences in t
ahe
2013/12/06 15:57:53
Done.
| |
| 699 callSelectorString = '"${namer.invocationName(callSelector)}"'; | |
| 700 } | |
| 701 tearOffInfo.add(new jsAst.LiteralString('$callSelectorString /* tearOffI nfo */')); | |
| 702 }, canTearOff); | |
| 703 } | |
| 704 | |
| 705 jsAst.Expression memberTypeExpression; | |
| 706 if ((canTearOff || canBeReflected) && !member.isGenerativeConstructorBody()) { | |
| 707 DartType memberType = member.computeType(compiler); | |
| 708 if (memberType.containsTypeVariables) { | |
| 709 jsAst.Expression thisAccess = js(r'this.$receiver'); | |
| 710 memberTypeExpression = | |
| 711 backend.rti.getSignatureEncoding(memberType, thisAccess); | |
| 712 } else { | |
| 713 memberTypeExpression = | |
| 714 js.toExpression(task.metadataEmitter.reifyType(memberType)); | |
| 620 } | 715 } |
| 716 } else { | |
| 717 memberTypeExpression = js('null'); | |
| 621 } | 718 } |
| 719 | |
| 720 expressions | |
| 721 ..addAll(tearOffInfo) | |
| 722 ..add((tearOffName == null || member.isAccessor()) | |
| 723 ? js("null") : js.string(tearOffName)) | |
| 724 ..add(requiredParameterCount) | |
| 725 ..add(optionalParameterCount) | |
| 726 ..add(memberTypeExpression) | |
| 727 ..addAll(task.metadataEmitter.reifyDefaultArguments(member)); | |
| 728 | |
| 729 if (canBeReflected || compiler.enabledFunctionApply) { | |
| 730 parameters.orderedForEachParameter((Element parameter) { | |
| 731 expressions.add(task.metadataEmitter.reifyName(parameter.name)); | |
| 732 }); | |
| 733 } | |
| 734 if (canBeReflected) { | |
| 735 jsAst.LiteralString reflectionName; | |
| 736 if (member.isConstructor()) { | |
| 737 String reflectionNameString = task.getReflectionName(member, name); | |
| 738 reflectionName = | |
| 739 new jsAst.LiteralString( | |
| 740 '"new ${Elements.reconstructConstructorName(member)}" /* $reflec tionNameString */'); | |
| 741 } else { | |
| 742 reflectionName = js.string(member.name); | |
| 743 } | |
| 744 expressions | |
| 745 ..add(reflectionName) | |
| 746 ..addAll(task.metadataEmitter.computeMetadata(member)); | |
| 747 } else if (isClosure && compiler.enabledFunctionApply) { | |
| 748 expressions.add(js.string(member.name)); | |
| 749 } | |
| 750 | |
| 751 builder.addProperty(name, js.toExpression(expressions)); | |
| 752 | |
| 753 // if (canTearOff) { | |
| 754 // emitDynamicFunctionGetter(member, builder.addProperty); | |
| 755 // } | |
| 622 } | 756 } |
| 623 | 757 |
| 624 void addMemberField(VariableElement member, ClassBuilder builder) { | 758 void addMemberField(VariableElement member, ClassBuilder builder) { |
| 625 // For now, do nothing. | 759 // For now, do nothing. |
| 626 } | 760 } |
| 627 } | 761 } |
| OLD | NEW |