| 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 |