OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Fletch project authors. Please see the AUTHORS file | |
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.md file. | |
4 | |
5 library fletchc.compiled_function; | |
6 | |
7 import 'package:compiler/src/constants/values.dart' show | |
8 ConstantValue; | |
9 | |
10 import 'package:compiler/src/constants/expressions.dart' show | |
11 ConstantExpression; | |
12 | |
13 import 'package:compiler/src/tree/tree.dart' show | |
14 Expression; | |
15 | |
16 import 'package:compiler/src/elements/elements.dart'; | |
17 | |
18 import 'fletch_constants.dart' show | |
19 FletchFunctionConstant, | |
20 FletchClassConstant; | |
21 | |
22 import 'package:compiler/src/universe/universe.dart' | |
23 show Selector; | |
24 | |
25 import '../bytecodes.dart' show | |
26 Bytecode; | |
27 | |
28 import 'compiled_class.dart' show | |
29 CompiledClass; | |
30 | |
31 import 'fletch_context.dart'; | |
32 import 'bytecode_builder.dart'; | |
33 import 'debug_info.dart'; | |
34 | |
35 enum CompiledFunctionKind { | |
36 NORMAL, | |
37 LAZY_FIELD_INITIALIZER, | |
38 INITIALIZER_LIST, | |
39 PARAMETER_STUB, | |
40 ACCESSOR | |
41 } | |
42 | |
43 class CompiledFunction { | |
44 final BytecodeBuilder builder; | |
45 final int methodId; | |
46 | |
47 /** | |
48 * The signature of the CompiledFunction. | |
49 * | |
50 * Som compiled functions does not have a signature (for example, generated | |
51 * accessors). | |
52 */ | |
53 final FunctionSignature signature; | |
54 | |
55 /** | |
56 * If the functions is an instance member, [memberOf] is set to the compiled | |
57 * class. | |
58 * | |
59 * If [memberOf] is set, the compiled function takes an 'this' argument in | |
60 * addition to that of [signature]. | |
61 */ | |
62 final CompiledClass memberOf; | |
63 final String name; | |
64 final Element element; | |
65 final Map<ConstantValue, int> constants = <ConstantValue, int>{}; | |
66 final Map<int, ConstantValue> functionConstantValues = <int, ConstantValue>{}; | |
67 final Map<int, ConstantValue> classConstantValues = <int, ConstantValue>{}; | |
68 final Map<Selector, CompiledFunction> parameterMappings = | |
69 <Selector, CompiledFunction>{}; | |
70 final int arity; | |
71 final CompiledFunctionKind kind; | |
72 | |
73 DebugInfo debugInfo; | |
74 | |
75 CompiledFunction(this.methodId, | |
76 this.name, | |
77 this.element, | |
78 FunctionSignature signature, | |
79 CompiledClass memberOf, | |
80 {this.kind: CompiledFunctionKind.NORMAL}) | |
81 : this.signature = signature, | |
82 this.memberOf = memberOf, | |
83 arity = signature.parameterCount + (memberOf != null ? 1 : 0), | |
84 builder = new BytecodeBuilder( | |
85 signature.parameterCount + (memberOf != null ? 1 : 0)); | |
86 | |
87 CompiledFunction.normal(this.methodId, int argumentCount) | |
88 : arity = argumentCount, | |
89 builder = new BytecodeBuilder(argumentCount), | |
90 kind = CompiledFunctionKind.NORMAL; | |
91 | |
92 CompiledFunction.lazyInit(this.methodId, | |
93 this.name, | |
94 this.element, | |
95 int argumentCount) | |
96 : arity = argumentCount, | |
97 builder = new BytecodeBuilder(argumentCount), | |
98 kind = CompiledFunctionKind.LAZY_FIELD_INITIALIZER; | |
99 | |
100 CompiledFunction.parameterStub(this.methodId, int argumentCount) | |
101 : arity = argumentCount, | |
102 builder = new BytecodeBuilder(argumentCount), | |
103 kind = CompiledFunctionKind.PARAMETER_STUB; | |
104 | |
105 CompiledFunction.accessor(this.methodId, bool setter) | |
106 : arity = setter ? 2 : 1, | |
107 builder = new BytecodeBuilder(setter ? 2 : 1), | |
108 kind = CompiledFunctionKind.ACCESSOR; | |
109 | |
110 void reuse() { | |
111 builder.reuse(); | |
112 constants.clear(); | |
113 functionConstantValues.clear(); | |
114 classConstantValues.clear(); | |
115 } | |
116 | |
117 bool get hasThisArgument => memberOf != null; | |
118 | |
119 bool get isLazyFieldInitializer { | |
120 return kind == CompiledFunctionKind.LAZY_FIELD_INITIALIZER; | |
121 } | |
122 | |
123 bool get isInitializerList { | |
124 return kind == CompiledFunctionKind.INITIALIZER_LIST; | |
125 } | |
126 | |
127 bool get isAccessor { | |
128 return kind == CompiledFunctionKind.ACCESSOR; | |
129 } | |
130 | |
131 bool get isParameterStub { | |
132 return kind == CompiledFunctionKind.PARAMETER_STUB; | |
133 } | |
134 | |
135 bool get isConstructor => element != null && element.isConstructor; | |
136 | |
137 int allocateConstant(ConstantValue constant) { | |
138 if (constant == null) throw "bad constant"; | |
139 return constants.putIfAbsent(constant, () => constants.length); | |
140 } | |
141 | |
142 int allocateConstantFromFunction(int methodId) { | |
143 FletchFunctionConstant constant = | |
144 functionConstantValues.putIfAbsent( | |
145 methodId, () => new FletchFunctionConstant(methodId)); | |
146 return allocateConstant(constant); | |
147 } | |
148 | |
149 int allocateConstantFromClass(int classId) { | |
150 FletchClassConstant constant = | |
151 classConstantValues.putIfAbsent( | |
152 classId, () => new FletchClassConstant(classId)); | |
153 return allocateConstant(constant); | |
154 } | |
155 | |
156 // TODO(ajohnsen): Remove this function when usage is avoided in | |
157 // FletchBackend. | |
158 void copyFrom(CompiledFunction function) { | |
159 builder.bytecodes.addAll(function.builder.bytecodes); | |
160 builder.catchRanges.addAll(function.builder.catchRanges); | |
161 constants.addAll(function.constants); | |
162 functionConstantValues.addAll(function.functionConstantValues); | |
163 classConstantValues.addAll(function.classConstantValues); | |
164 } | |
165 | |
166 bool matchesSelector(Selector selector) { | |
167 if (!canBeCalledAs(selector)) return false; | |
168 if (selector.namedArguments.length != signature.optionalParameterCount) { | |
169 return false; | |
170 } | |
171 int index = 0; | |
172 bool match = true; | |
173 for (var parameter in signature.orderedOptionalParameters) { | |
174 if (parameter.name != selector.namedArguments[index++]) match = false; | |
175 } | |
176 return match; | |
177 } | |
178 | |
179 // TODO(ajohnsen): Remove and use the one one Selector, when it takes a | |
180 // FunctionSignature directly. | |
181 // This is raw copy of Selector.signaturesApplies. | |
182 bool canBeCalledAs(Selector selector) { | |
183 if (selector.argumentCount > signature.parameterCount) return false; | |
184 int requiredParameterCount = signature.requiredParameterCount; | |
185 int optionalParameterCount = signature.optionalParameterCount; | |
186 if (selector.positionalArgumentCount < requiredParameterCount) return false; | |
187 | |
188 if (!signature.optionalParametersAreNamed) { | |
189 // We have already checked that the number of arguments are | |
190 // not greater than the number of signature. Therefore the | |
191 // number of positional arguments are not greater than the | |
192 // number of signature. | |
193 assert(selector.positionalArgumentCount <= signature.parameterCount); | |
194 return selector.namedArguments.isEmpty; | |
195 } else { | |
196 if (selector.positionalArgumentCount > requiredParameterCount) { | |
197 return false; | |
198 } | |
199 assert(selector.positionalArgumentCount == requiredParameterCount); | |
200 if (selector.namedArgumentCount > optionalParameterCount) return false; | |
201 Set<String> nameSet = new Set<String>(); | |
202 signature.optionalParameters.forEach((Element element) { | |
203 nameSet.add(element.name); | |
204 }); | |
205 for (String name in selector.namedArguments) { | |
206 if (!nameSet.contains(name)) return false; | |
207 // TODO(5213): By removing from the set we are checking | |
208 // that we are not passing the name twice. We should have this | |
209 // check in the resolver also. | |
210 nameSet.remove(name); | |
211 } | |
212 return true; | |
213 } | |
214 } | |
215 | |
216 CompiledFunction createParameterMappingFor( | |
217 Selector selector, | |
218 FletchContext context) { | |
219 return parameterMappings.putIfAbsent(selector, () { | |
220 assert(canBeCalledAs(selector)); | |
221 int arity = selector.argumentCount; | |
222 if (hasThisArgument) arity++; | |
223 | |
224 CompiledFunction compiledFunction = new CompiledFunction.parameterStub( | |
225 context.backend.functions.length, | |
226 arity); | |
227 context.backend.functions.add(compiledFunction); | |
228 | |
229 BytecodeBuilder builder = compiledFunction.builder; | |
230 | |
231 void loadInitializerOrNull(ParameterElement parameter) { | |
232 Expression initializer = parameter.initializer; | |
233 if (initializer != null) { | |
234 ConstantExpression expression = context.compileConstant( | |
235 initializer, | |
236 parameter.memberContext.resolvedAst.elements, | |
237 isConst: true); | |
238 int constId = compiledFunction.allocateConstant( | |
239 context.getConstantValue(expression)); | |
240 builder.loadConst(constId); | |
241 } else { | |
242 builder.loadLiteralNull(); | |
243 } | |
244 } | |
245 | |
246 // Load this. | |
247 if (hasThisArgument) builder.loadParameter(0); | |
248 | |
249 int index = hasThisArgument ? 1 : 0; | |
250 signature.orderedForEachParameter((ParameterElement parameter) { | |
251 if (!parameter.isOptional) { | |
252 builder.loadParameter(index); | |
253 } else if (parameter.isNamed) { | |
254 int parameterIndex = selector.namedArguments.indexOf(parameter.name); | |
255 if (parameterIndex >= 0) { | |
256 if (hasThisArgument) parameterIndex++; | |
257 int position = selector.positionalArgumentCount + parameterIndex; | |
258 builder.loadParameter(position); | |
259 } else { | |
260 loadInitializerOrNull(parameter); | |
261 } | |
262 } else { | |
263 if (index < arity) { | |
264 builder.loadParameter(index); | |
265 } else { | |
266 loadInitializerOrNull(parameter); | |
267 } | |
268 } | |
269 index++; | |
270 }); | |
271 | |
272 // TODO(ajohnsen): We have to be extra careful when overriding a | |
273 // method that takes optional arguments. We really should | |
274 // enumerate all the stubs in the superclasses and make sure | |
275 // they're overridden. | |
276 int constId = compiledFunction.allocateConstantFromFunction(methodId); | |
277 builder | |
278 ..invokeStatic(constId, index) | |
279 ..ret() | |
280 ..methodEnd(); | |
281 | |
282 if (memberOf != null) { | |
283 int fletchSelector = context.toFletchSelector(selector); | |
284 memberOf.addToMethodTable(fletchSelector, compiledFunction); | |
285 } | |
286 | |
287 return compiledFunction; | |
288 }); | |
289 } | |
290 | |
291 String verboseToString() { | |
292 StringBuffer sb = new StringBuffer(); | |
293 | |
294 sb.writeln("Method $methodId, Arity=${builder.functionArity}"); | |
295 sb.writeln("Constants:"); | |
296 constants.forEach((constant, int index) { | |
297 if (constant is ConstantValue) { | |
298 constant = constant.toStructuredString(); | |
299 } | |
300 sb.writeln(" #$index: $constant"); | |
301 }); | |
302 | |
303 sb.writeln("Bytecodes:"); | |
304 int offset = 0; | |
305 for (Bytecode bytecode in builder.bytecodes) { | |
306 sb.writeln(" $offset: $bytecode"); | |
307 offset += bytecode.size; | |
308 } | |
309 | |
310 return '$sb'; | |
311 } | |
312 } | |
OLD | NEW |