OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.js_emitter.class_stub_generator; | 5 library dart2js.js_emitter.class_stub_generator; |
6 | 6 |
7 import '../common/names.dart' show Identifiers; | 7 import '../common/names.dart' show Identifiers; |
8 import '../compiler.dart' show Compiler; | 8 import '../common_elements.dart' show CommonElements; |
9 import '../elements/entities.dart'; | 9 import '../elements/entities.dart'; |
10 import '../js/js.dart' as jsAst; | 10 import '../js/js.dart' as jsAst; |
11 import '../js/js.dart' show js; | 11 import '../js/js.dart' show js; |
12 import '../js_backend/js_backend.dart' show JavaScriptBackend, Namer; | 12 import '../js_backend/namer.dart' show Namer; |
| 13 import '../js_backend/interceptor_data.dart' show InterceptorData; |
| 14 import '../options.dart'; |
13 import '../universe/selector.dart' show Selector; | 15 import '../universe/selector.dart' show Selector; |
14 import '../universe/world_builder.dart' | 16 import '../universe/world_builder.dart' |
15 show CodegenWorldBuilder, SelectorConstraints; | 17 show CodegenWorldBuilder, SelectorConstraints; |
16 import '../world.dart' show ClosedWorld; | 18 import '../world.dart' show ClosedWorld; |
17 | 19 |
| 20 import 'code_emitter_task.dart'; |
18 import 'model.dart'; | 21 import 'model.dart'; |
19 | 22 |
20 class ClassStubGenerator { | 23 class ClassStubGenerator { |
21 final Namer namer; | 24 final Namer _namer; |
22 final JavaScriptBackend backend; | 25 final CodegenWorldBuilder _worldBuilder; |
23 final CodegenWorldBuilder worldBuilder; | 26 final ClosedWorld _closedWorld; |
24 final ClosedWorld closedWorld; | 27 final InterceptorData _interceptorData; |
25 final bool enableMinification; | 28 final bool enableMinification; |
| 29 final Emitter _emitter; |
| 30 final CommonElements _commonElements; |
26 | 31 |
27 ClassStubGenerator( | 32 ClassStubGenerator(this._emitter, this._commonElements, this._namer, |
28 this.namer, this.backend, this.worldBuilder, this.closedWorld, | 33 this._worldBuilder, this._interceptorData, this._closedWorld, |
29 {this.enableMinification}); | 34 {this.enableMinification}); |
30 | 35 |
31 jsAst.Expression generateClassConstructor( | 36 jsAst.Expression generateClassConstructor( |
32 ClassEntity classElement, Iterable<jsAst.Name> fields, bool hasRtiField) { | 37 ClassEntity classElement, Iterable<jsAst.Name> fields, bool hasRtiField) { |
33 // TODO(sra): Implement placeholders in VariableDeclaration position: | 38 // TODO(sra): Implement placeholders in VariableDeclaration position: |
34 // | 39 // |
35 // String constructorName = namer.getNameOfClass(classElement); | 40 // String constructorName = namer.getNameOfClass(classElement); |
36 // return js.statement('function #(#) { #; }', | 41 // return js.statement('function #(#) { #; }', |
37 // [ constructorName, fields, | 42 // [ constructorName, fields, |
38 // fields.map( | 43 // fields.map( |
39 // (name) => js('this.# = #', [name, name]))])); | 44 // (name) => js('this.# = #', [name, name]))])); |
40 var typeParameters = const <jsAst.Parameter>[]; | 45 var typeParameters = const <jsAst.Parameter>[]; |
41 var typeInits = const <jsAst.Expression>[]; | 46 var typeInits = const <jsAst.Expression>[]; |
42 if (hasRtiField) { | 47 if (hasRtiField) { |
43 var rtiName = namer.rtiFieldJsName; | 48 var rtiName = _namer.rtiFieldJsName; |
44 typeParameters = rtiName; | 49 typeParameters = rtiName; |
45 typeInits = js('this.# = #', [rtiName, rtiName]); | 50 typeInits = js('this.# = #', [rtiName, rtiName]); |
46 } | 51 } |
47 return js('function(#, #) { #; #; this.#();}', [ | 52 return js('function(#, #) { #; #; this.#();}', [ |
48 fields, | 53 fields, |
49 typeParameters, | 54 typeParameters, |
50 fields.map((name) => js('this.# = #', [name, name])), | 55 fields.map((name) => js('this.# = #', [name, name])), |
51 typeInits, | 56 typeInits, |
52 namer.deferredAction | 57 _namer.deferredAction |
53 ]); | 58 ]); |
54 } | 59 } |
55 | 60 |
56 jsAst.Expression generateGetter(MemberEntity member, jsAst.Name fieldName) { | 61 jsAst.Expression generateGetter(MemberEntity member, jsAst.Name fieldName) { |
57 ClassEntity cls = member.enclosingClass; | 62 ClassEntity cls = member.enclosingClass; |
58 String receiver = | 63 String receiver = |
59 backend.interceptorData.isInterceptedClass(cls) ? 'receiver' : 'this'; | 64 _interceptorData.isInterceptedClass(cls) ? 'receiver' : 'this'; |
60 List<String> args = | 65 List<String> args = |
61 backend.interceptorData.isInterceptedMethod(member) ? ['receiver'] : []; | 66 _interceptorData.isInterceptedMethod(member) ? ['receiver'] : []; |
62 return js('function(#) { return #.# }', [args, receiver, fieldName]); | 67 return js('function(#) { return #.# }', [args, receiver, fieldName]); |
63 } | 68 } |
64 | 69 |
65 jsAst.Expression generateSetter(MemberEntity member, jsAst.Name fieldName) { | 70 jsAst.Expression generateSetter(MemberEntity member, jsAst.Name fieldName) { |
66 ClassEntity cls = member.enclosingClass; | 71 ClassEntity cls = member.enclosingClass; |
67 String receiver = | 72 String receiver = |
68 backend.interceptorData.isInterceptedClass(cls) ? 'receiver' : 'this'; | 73 _interceptorData.isInterceptedClass(cls) ? 'receiver' : 'this'; |
69 List<String> args = | 74 List<String> args = |
70 backend.interceptorData.isInterceptedMethod(member) ? ['receiver'] : []; | 75 _interceptorData.isInterceptedMethod(member) ? ['receiver'] : []; |
71 // TODO(floitsch): remove 'return'? | 76 // TODO(floitsch): remove 'return'? |
72 return js( | 77 return js( |
73 'function(#, v) { return #.# = v; }', [args, receiver, fieldName]); | 78 'function(#, v) { return #.# = v; }', [args, receiver, fieldName]); |
74 } | 79 } |
75 | 80 |
76 /** | 81 /** |
77 * Documentation wanted -- johnniwinther | 82 * Documentation wanted -- johnniwinther |
78 * | 83 * |
79 * Invariant: [member] must be a declaration element. | 84 * Invariant: [member] must be a declaration element. |
80 */ | 85 */ |
81 Map<jsAst.Name, jsAst.Expression> generateCallStubsForGetter( | 86 Map<jsAst.Name, jsAst.Expression> generateCallStubsForGetter( |
82 MemberEntity member, Map<Selector, SelectorConstraints> selectors) { | 87 MemberEntity member, Map<Selector, SelectorConstraints> selectors) { |
83 // If the method is intercepted, the stub gets the | 88 // If the method is intercepted, the stub gets the |
84 // receiver explicitely and we need to pass it to the getter call. | 89 // receiver explicitely and we need to pass it to the getter call. |
85 bool isInterceptedMethod = | 90 bool isInterceptedMethod = _interceptorData.isInterceptedMethod(member); |
86 backend.interceptorData.isInterceptedMethod(member); | |
87 bool isInterceptedClass = | 91 bool isInterceptedClass = |
88 backend.interceptorData.isInterceptedClass(member.enclosingClass); | 92 _interceptorData.isInterceptedClass(member.enclosingClass); |
89 | 93 |
90 const String receiverArgumentName = r'$receiver'; | 94 const String receiverArgumentName = r'$receiver'; |
91 | 95 |
92 jsAst.Expression buildGetter() { | 96 jsAst.Expression buildGetter() { |
93 jsAst.Expression receiver = | 97 jsAst.Expression receiver = |
94 js(isInterceptedClass ? receiverArgumentName : 'this'); | 98 js(isInterceptedClass ? receiverArgumentName : 'this'); |
95 if (member.isGetter) { | 99 if (member.isGetter) { |
96 jsAst.Name getterName = namer.getterForElement(member); | 100 jsAst.Name getterName = _namer.getterForElement(member); |
97 if (isInterceptedMethod) { | 101 if (isInterceptedMethod) { |
98 return js('this.#(#)', [getterName, receiver]); | 102 return js('this.#(#)', [getterName, receiver]); |
99 } | 103 } |
100 return js('#.#()', [receiver, getterName]); | 104 return js('#.#()', [receiver, getterName]); |
101 } else { | 105 } else { |
102 jsAst.Name fieldName = namer.instanceFieldPropertyName(member); | 106 jsAst.Name fieldName = _namer.instanceFieldPropertyName(member); |
103 return js('#.#', [receiver, fieldName]); | 107 return js('#.#', [receiver, fieldName]); |
104 } | 108 } |
105 } | 109 } |
106 | 110 |
107 Map<jsAst.Name, jsAst.Expression> generatedStubs = | 111 Map<jsAst.Name, jsAst.Expression> generatedStubs = |
108 <jsAst.Name, jsAst.Expression>{}; | 112 <jsAst.Name, jsAst.Expression>{}; |
109 | 113 |
110 // Two selectors may match but differ only in type. To avoid generating | 114 // Two selectors may match but differ only in type. To avoid generating |
111 // identical stubs for each we track untyped selectors which already have | 115 // identical stubs for each we track untyped selectors which already have |
112 // stubs. | 116 // stubs. |
113 Set<Selector> generatedSelectors = new Set<Selector>(); | 117 Set<Selector> generatedSelectors = new Set<Selector>(); |
114 for (Selector selector in selectors.keys) { | 118 for (Selector selector in selectors.keys) { |
115 if (generatedSelectors.contains(selector)) continue; | 119 if (generatedSelectors.contains(selector)) continue; |
116 if (!selector.appliesUnnamed(member)) continue; | 120 if (!selector.appliesUnnamed(member)) continue; |
117 if (selectors[selector].applies(member, selector, closedWorld)) { | 121 if (selectors[selector].applies(member, selector, _closedWorld)) { |
118 generatedSelectors.add(selector); | 122 generatedSelectors.add(selector); |
119 | 123 |
120 jsAst.Name invocationName = namer.invocationName(selector); | 124 jsAst.Name invocationName = _namer.invocationName(selector); |
121 Selector callSelector = new Selector.callClosureFrom(selector); | 125 Selector callSelector = new Selector.callClosureFrom(selector); |
122 jsAst.Name closureCallName = namer.invocationName(callSelector); | 126 jsAst.Name closureCallName = _namer.invocationName(callSelector); |
123 | 127 |
124 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; | 128 List<jsAst.Parameter> parameters = <jsAst.Parameter>[]; |
125 List<jsAst.Expression> arguments = <jsAst.Expression>[]; | 129 List<jsAst.Expression> arguments = <jsAst.Expression>[]; |
126 if (isInterceptedMethod) { | 130 if (isInterceptedMethod) { |
127 parameters.add(new jsAst.Parameter(receiverArgumentName)); | 131 parameters.add(new jsAst.Parameter(receiverArgumentName)); |
128 } | 132 } |
129 | 133 |
130 for (int i = 0; i < selector.argumentCount; i++) { | 134 for (int i = 0; i < selector.argumentCount; i++) { |
131 String name = 'arg$i'; | 135 String name = 'arg$i'; |
132 parameters.add(new jsAst.Parameter(name)); | 136 parameters.add(new jsAst.Parameter(name)); |
133 arguments.add(js('#', name)); | 137 arguments.add(js('#', name)); |
134 } | 138 } |
135 | 139 |
136 jsAst.Fun function = js('function(#) { return #.#(#); }', | 140 jsAst.Fun function = js('function(#) { return #.#(#); }', |
137 [parameters, buildGetter(), closureCallName, arguments]); | 141 [parameters, buildGetter(), closureCallName, arguments]); |
138 | 142 |
139 generatedStubs[invocationName] = function; | 143 generatedStubs[invocationName] = function; |
140 } | 144 } |
141 } | 145 } |
142 | 146 |
143 return generatedStubs; | 147 return generatedStubs; |
144 } | 148 } |
145 | 149 |
146 Map<jsAst.Name, Selector> computeSelectorsForNsmHandlers() { | 150 Map<jsAst.Name, Selector> computeSelectorsForNsmHandlers() { |
147 Map<jsAst.Name, Selector> jsNames = <jsAst.Name, Selector>{}; | 151 Map<jsAst.Name, Selector> jsNames = <jsAst.Name, Selector>{}; |
148 | 152 |
149 // Do not generate no such method handlers if there is no class. | 153 // Do not generate no such method handlers if there is no class. |
150 if (worldBuilder.directlyInstantiatedClasses.isEmpty) { | 154 if (_worldBuilder.directlyInstantiatedClasses.isEmpty) { |
151 return jsNames; | 155 return jsNames; |
152 } | 156 } |
153 | 157 |
154 void addNoSuchMethodHandlers( | 158 void addNoSuchMethodHandlers( |
155 String ignore, Map<Selector, SelectorConstraints> selectors) { | 159 String ignore, Map<Selector, SelectorConstraints> selectors) { |
156 for (Selector selector in selectors.keys) { | 160 for (Selector selector in selectors.keys) { |
157 SelectorConstraints maskSet = selectors[selector]; | 161 SelectorConstraints maskSet = selectors[selector]; |
158 if (maskSet.needsNoSuchMethodHandling(selector, closedWorld)) { | 162 if (maskSet.needsNoSuchMethodHandling(selector, _closedWorld)) { |
159 jsAst.Name jsName = namer.invocationMirrorInternalName(selector); | 163 jsAst.Name jsName = _namer.invocationMirrorInternalName(selector); |
160 jsNames[jsName] = selector; | 164 jsNames[jsName] = selector; |
161 } | 165 } |
162 } | 166 } |
163 } | 167 } |
164 | 168 |
165 worldBuilder.forEachInvokedName(addNoSuchMethodHandlers); | 169 _worldBuilder.forEachInvokedName(addNoSuchMethodHandlers); |
166 worldBuilder.forEachInvokedGetter(addNoSuchMethodHandlers); | 170 _worldBuilder.forEachInvokedGetter(addNoSuchMethodHandlers); |
167 worldBuilder.forEachInvokedSetter(addNoSuchMethodHandlers); | 171 _worldBuilder.forEachInvokedSetter(addNoSuchMethodHandlers); |
168 return jsNames; | 172 return jsNames; |
169 } | 173 } |
170 | 174 |
171 StubMethod generateStubForNoSuchMethod(jsAst.Name name, Selector selector) { | 175 StubMethod generateStubForNoSuchMethod(jsAst.Name name, Selector selector) { |
172 // Values match JSInvocationMirror in js-helper library. | 176 // Values match JSInvocationMirror in js-helper library. |
173 int type = selector.invocationMirrorKind; | 177 int type = selector.invocationMirrorKind; |
174 List<String> parameterNames = | 178 List<String> parameterNames = |
175 new List.generate(selector.argumentCount, (i) => '\$$i'); | 179 new List.generate(selector.argumentCount, (i) => '\$$i'); |
176 | 180 |
177 List<jsAst.Expression> argNames = selector.callStructure | 181 List<jsAst.Expression> argNames = selector.callStructure |
178 .getOrderedNamedArguments() | 182 .getOrderedNamedArguments() |
179 .map((String name) => js.string(name)) | 183 .map((String name) => js.string(name)) |
180 .toList(); | 184 .toList(); |
181 | 185 |
182 jsAst.Name methodName = namer.asName(selector.invocationMirrorMemberName); | 186 jsAst.Name methodName = _namer.asName(selector.invocationMirrorMemberName); |
183 jsAst.Name internalName = namer.invocationMirrorInternalName(selector); | 187 jsAst.Name internalName = _namer.invocationMirrorInternalName(selector); |
184 | 188 |
185 assert( | 189 assert(_interceptorData.isInterceptedName(Identifiers.noSuchMethod_)); |
186 backend.interceptorData.isInterceptedName(Identifiers.noSuchMethod_)); | 190 bool isIntercepted = _interceptorData.isInterceptedName(selector.name); |
187 bool isIntercepted = | |
188 backend.interceptorData.isInterceptedName(selector.name); | |
189 jsAst.Expression expression = js( | 191 jsAst.Expression expression = js( |
190 '''this.#noSuchMethodName(#receiver, | 192 '''this.#noSuchMethodName(#receiver, |
191 #createInvocationMirror(#methodName, | 193 #createInvocationMirror(#methodName, |
192 #internalName, | 194 #internalName, |
193 #type, | 195 #type, |
194 #arguments, | 196 #arguments, |
195 #namedArguments))''', | 197 #namedArguments))''', |
196 { | 198 { |
197 'receiver': isIntercepted ? r'$receiver' : 'this', | 199 'receiver': isIntercepted ? r'$receiver' : 'this', |
198 'noSuchMethodName': namer.noSuchMethodName, | 200 'noSuchMethodName': _namer.noSuchMethodName, |
199 'createInvocationMirror': backend.emitter.staticFunctionAccess( | 201 'createInvocationMirror': _emitter |
200 backend.commonElements.createInvocationMirror), | 202 .staticFunctionAccess(_commonElements.createInvocationMirror), |
201 'methodName': | 203 'methodName': |
202 js.quoteName(enableMinification ? internalName : methodName), | 204 js.quoteName(enableMinification ? internalName : methodName), |
203 'internalName': js.quoteName(internalName), | 205 'internalName': js.quoteName(internalName), |
204 'type': js.number(type), | 206 'type': js.number(type), |
205 'arguments': | 207 'arguments': |
206 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), | 208 new jsAst.ArrayInitializer(parameterNames.map(js).toList()), |
207 'namedArguments': new jsAst.ArrayInitializer(argNames) | 209 'namedArguments': new jsAst.ArrayInitializer(argNames) |
208 }); | 210 }); |
209 | 211 |
210 jsAst.Expression function; | 212 jsAst.Expression function; |
(...skipping 14 matching lines...) Expand all Loading... |
225 /// `tearOff` takes the following arguments: | 227 /// `tearOff` takes the following arguments: |
226 /// * `funcs`: a list of functions. These are the functions representing the | 228 /// * `funcs`: a list of functions. These are the functions representing the |
227 /// member that is torn off. There can be more than one, since a member | 229 /// member that is torn off. There can be more than one, since a member |
228 /// can have several stubs. | 230 /// can have several stubs. |
229 /// Each function must have the `$callName` property set. | 231 /// Each function must have the `$callName` property set. |
230 /// * `reflectionInfo`: contains reflective information, and the function | 232 /// * `reflectionInfo`: contains reflective information, and the function |
231 /// type. TODO(floitsch): point to where this is specified. | 233 /// type. TODO(floitsch): point to where this is specified. |
232 /// * `isStatic`. | 234 /// * `isStatic`. |
233 /// * `name`. | 235 /// * `name`. |
234 /// * `isIntercepted. | 236 /// * `isIntercepted. |
235 List<jsAst.Statement> buildTearOffCode(JavaScriptBackend backend) { | 237 List<jsAst.Statement> buildTearOffCode(CompilerOptions options, Emitter emitter, |
236 Namer namer = backend.namer; | 238 Namer namer, CommonElements commonElements) { |
237 Compiler compiler = backend.compiler; | 239 FunctionEntity closureFromTearOff = commonElements.closureFromTearOff; |
238 | |
239 FunctionEntity closureFromTearOff = backend.commonElements.closureFromTearOff; | |
240 jsAst.Expression tearOffAccessExpression; | 240 jsAst.Expression tearOffAccessExpression; |
241 jsAst.Expression tearOffGlobalObjectString; | 241 jsAst.Expression tearOffGlobalObjectString; |
242 jsAst.Expression tearOffGlobalObject; | 242 jsAst.Expression tearOffGlobalObject; |
243 if (closureFromTearOff != null) { | 243 if (closureFromTearOff != null) { |
244 tearOffAccessExpression = | 244 tearOffAccessExpression = emitter.staticFunctionAccess(closureFromTearOff); |
245 backend.emitter.staticFunctionAccess(closureFromTearOff); | |
246 tearOffGlobalObject = | 245 tearOffGlobalObject = |
247 js.stringPart(namer.globalObjectForMethod(closureFromTearOff)); | 246 js.stringPart(namer.globalObjectForMethod(closureFromTearOff)); |
248 tearOffGlobalObjectString = | 247 tearOffGlobalObjectString = |
249 js.string(namer.globalObjectForMethod(closureFromTearOff)); | 248 js.string(namer.globalObjectForMethod(closureFromTearOff)); |
250 } else { | 249 } else { |
251 // Default values for mocked-up test libraries. | 250 // Default values for mocked-up test libraries. |
252 tearOffAccessExpression = | 251 tearOffAccessExpression = |
253 js(r'''function() { throw "Helper 'closureFromTearOff' missing." }'''); | 252 js(r'''function() { throw "Helper 'closureFromTearOff' missing." }'''); |
254 tearOffGlobalObjectString = js.string('MissingHelperFunction'); | 253 tearOffGlobalObjectString = js.string('MissingHelperFunction'); |
255 tearOffGlobalObject = js( | 254 tearOffGlobalObject = js( |
256 r'''(function() { throw "Helper 'closureFromTearOff' missing." })()'''); | 255 r'''(function() { throw "Helper 'closureFromTearOff' missing." })()'''); |
257 } | 256 } |
258 | 257 |
259 jsAst.Statement tearOffGetter; | 258 jsAst.Statement tearOffGetter; |
260 if (!compiler.options.useContentSecurityPolicy) { | 259 if (!options.useContentSecurityPolicy) { |
261 jsAst.Expression tearOffAccessText = new jsAst.UnparsedNode( | 260 jsAst.Expression tearOffAccessText = |
262 tearOffAccessExpression, compiler.options, false); | 261 new jsAst.UnparsedNode(tearOffAccessExpression, options, false); |
263 tearOffGetter = js.statement( | 262 tearOffGetter = js.statement( |
264 ''' | 263 ''' |
265 function tearOffGetter(funcs, reflectionInfo, name, isIntercepted) { | 264 function tearOffGetter(funcs, reflectionInfo, name, isIntercepted) { |
266 return isIntercepted | 265 return isIntercepted |
267 ? new Function("funcs", "reflectionInfo", "name", | 266 ? new Function("funcs", "reflectionInfo", "name", |
268 #tearOffGlobalObjectString, "c", | 267 #tearOffGlobalObjectString, "c", |
269 "return function tearOff_" + name + (functionCounter++) + "(x) {" + | 268 "return function tearOff_" + name + (functionCounter++) + "(x) {" + |
270 "if (c === null) c = " + #tearOffAccessText + "(" + | 269 "if (c === null) c = " + #tearOffAccessText + "(" + |
271 "this, funcs, reflectionInfo, false, [x], name);" + | 270 "this, funcs, reflectionInfo, false, [x], name);" + |
272 "return new c(this, funcs[0], x, name);" + | 271 "return new c(this, funcs[0], x, name);" + |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 if (cache === void 0) cache = #tearOff( | 312 if (cache === void 0) cache = #tearOff( |
314 this, funcs, reflectionInfo, true, [], name).prototype; | 313 this, funcs, reflectionInfo, true, [], name).prototype; |
315 return cache; | 314 return cache; |
316 } | 315 } |
317 : tearOffGetter(funcs, reflectionInfo, name, isIntercepted); | 316 : tearOffGetter(funcs, reflectionInfo, name, isIntercepted); |
318 }''', | 317 }''', |
319 {'tearOff': tearOffAccessExpression}); | 318 {'tearOff': tearOffAccessExpression}); |
320 | 319 |
321 return <jsAst.Statement>[tearOffGetter, tearOff]; | 320 return <jsAst.Statement>[tearOffGetter, tearOff]; |
322 } | 321 } |
OLD | NEW |