Chromium Code Reviews| OLD | NEW | 
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2017, the Dart 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 file. | |
| 4 | |
| 5 /// Logic to build unlinked summaries. | |
| 6 library summary.src.summary_builder; | |
| 7 | |
| 8 import 'expression_serializer.dart'; | |
| 9 import 'model.dart'; | |
| 10 import 'package:front_end/src/fasta/parser/class_member_parser.dart'; | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
I'm surprised that the sorting of imports doesn't
 
Paul Berry
2017/03/09 23:26:23
Oops--I added these imports in any old location be
 
 | |
| 11 import 'package:front_end/src/fasta/parser/identifier_context.dart'; | |
| 12 import 'package:front_end/src/fasta/parser/parser.dart'; | |
| 13 import 'package:front_end/src/fasta/scanner.dart'; | |
| 14 import 'package:front_end/src/fasta/scanner/token_constants.dart'; | |
| 15 import 'stack_listener.dart'; | |
| 16 | |
| 17 /// Create an unlinked summary given a null-terminated byte buffer with the | |
| 18 /// contents of a file. | |
| 19 UnlinkedUnit summarize(Uri uri, List<int> contents) { | |
| 20 var listener = new SummaryBuilder(uri); | |
| 21 var parser = new ClassMemberParser(listener); | |
| 22 parser.parseUnit(scan(contents).tokens); | |
| 23 return listener.topScope.unit; | |
| 24 } | |
| 25 | |
| 26 /// A listener of parser events that builds summary information as parsing | |
| 27 /// progresses. | |
| 28 class SummaryBuilder extends StackListener { | |
| 29 /// Whether 'dart:core' was imported explicitly by the current unit. | |
| 30 bool isDartCoreImported = false; | |
| 31 | |
| 32 /// Whether the current unit is part of 'dart:core'. | |
| 33 bool isCoreLibrary = false; | |
| 34 | |
| 35 /// Topmost scope. | |
| 36 TopScope topScope; | |
| 37 | |
| 38 /// Current scope where name references are resolved from. | |
| 39 Scope scope; | |
| 40 | |
| 41 /// Helper to build constant expressions. | |
| 42 final ConstExpressionBuilder constBuilder; | |
| 43 | |
| 44 /// Helper to build initializer expressions. | |
| 45 final InitializerBuilder initializerBuilder; | |
| 46 | |
| 47 /// Whether the current initializer has a type declared. | |
| 48 /// | |
| 49 /// Because initializers are only used for strong-mode inference, we can skip | |
| 50 /// parsing and building initializer expressions when a type is declared. | |
| 51 bool typeSeen = false; | |
| 52 | |
| 53 /// Whether we are currently in the context of a const expression. | |
| 54 bool inConstContext = false; | |
| 55 | |
| 56 /// Whether we need to parse the initializer of a declaration. | |
| 57 bool get needInitializer => !typeSeen || inConstContext; | |
| 58 | |
| 59 /// Uri of the file currently being processed, used for error reporting only. | |
| 60 final Uri uri; | |
| 61 | |
| 62 | |
| 63 /// Summaries preassign slots for computed information, this is the next | |
| 64 /// available slot. | |
| 65 int _slots = 0; | |
| 66 | |
| 67 SummaryBuilder(Uri uri) | |
| 68 : uri = uri, | |
| 69 constBuilder = new ConstExpressionBuilder(uri), | |
| 70 initializerBuilder = new InitializerBuilder(uri); | |
| 71 | |
| 72 void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | |
| 73 debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | |
| 74 } | |
| 75 | |
| 76 void beginCompilationUnit(Token token) { | |
| 77 scope = topScope = new TopScope(); | |
| 78 } | |
| 79 | |
| 80 void endCompilationUnit(int count, Token token) { | |
| 81 if (!isDartCoreImported) { | |
| 82 topScope.unit.imports.add(new UnlinkedImportBuilder(isImplicit: true)); | |
| 83 } | |
| 84 | |
| 85 topScope.expandLazyReferences(); | |
| 86 | |
| 87 // TODO(sigmund): could this be be optional: done by whoever consumes it? | |
| 88 if (const bool.fromEnvironment('SKIP_API')) return; | |
| 89 var apiSignature = new ApiSignature(); | |
| 90 topScope.unit.collectApiSignature(apiSignature); | |
| 91 topScope.unit.apiSignature = apiSignature.toByteList(); | |
| 92 } | |
| 93 | |
| 94 // Directives: imports, exports, parts | |
| 95 | |
| 96 void endHide(_) { | |
| 97 // ignore: strong_mode_down_cast_composite | |
| 98 push(new UnlinkedCombinatorBuilder(hides: pop())); | |
| 99 } | |
| 100 | |
| 101 void endShow(_) { | |
| 102 // ignore: strong_mode_down_cast_composite | |
| 103 push(new UnlinkedCombinatorBuilder(shows: pop())); | |
| 104 } | |
| 105 | |
| 106 void endCombinators(int count) { | |
| 107 debugEvent("Combinators"); | |
| 108 push(popList(count) ?? NullValue.Combinators); | |
| 109 } | |
| 110 | |
| 111 void endConditionalUri(Token ifKeyword, Token equalitySign) { | |
| 112 String dottedName = pop(); | |
| 113 String value = pop(); | |
| 114 String uri = pop(); | |
| 115 uri = uri.substring(1, uri.length - 1); | |
| 116 push(new UnlinkedConfigurationBuilder( | |
| 117 name: dottedName, value: value, uri: uri)); | |
| 118 } | |
| 119 | |
| 120 void endConditionalUris(int count) { | |
| 121 push(popList(count) ?? const <UnlinkedConfigurationBuilder>[]); | |
| 122 } | |
| 123 | |
| 124 void endExport(Token exportKeyword, Token semicolon) { | |
| 125 debugEvent("Export"); | |
| 126 // ignore: strong_mode_down_cast_composite | |
| 127 List<UnlinkedCombinator> combinators = pop(); | |
| 128 // ignore: strong_mode_down_cast_composite | |
| 129 List<UnlinkedConfiguration> conditionalUris = pop(); | |
| 130 String uri = pop(); | |
| 131 // ignore: strong_mode_down_cast_composite | |
| 132 List<UnlinkedExpr> metadata = pop(); | |
| 133 topScope.unit.exports.add(new UnlinkedExportNonPublicBuilder(annotations: me tadata)); | |
| 134 topScope.publicNamespace.exports.add(new UnlinkedExportPublicBuilder( | |
| 135 uri: uri, | |
| 136 combinators: combinators, | |
| 137 configurations: conditionalUris)); | |
| 138 checkEmpty(); | |
| 139 } | |
| 140 | |
| 141 void endImport(Token importKeyword, Token deferredKeyword, Token asKeyword, | |
| 142 Token semicolon) { | |
| 143 debugEvent("endImport"); | |
| 144 // ignore: strong_mode_down_cast_composite | |
| 145 List<UnlinkedCombinator> combinators = pop(); | |
| 146 String prefix = popIfNotNull(asKeyword); | |
| 147 int prefixIndex = prefix == null ? null | |
| 148 : topScope.serializeReference(null, prefix); | |
| 149 // ignore: strong_mode_down_cast_composite | |
| 150 List<UnlinkedConfiguration> conditionalUris = pop(); | |
| 151 String uri = pop(); | |
| 152 // ignore: strong_mode_down_cast_composite | |
| 153 List<UnlinkedExpr> metadata = pop(); // metadata | |
| 154 | |
| 155 topScope.unit.imports.add(new UnlinkedImportBuilder( | |
| 156 uri: uri, | |
| 157 prefixReference: prefixIndex, | |
| 158 combinators: combinators, | |
| 159 configurations: conditionalUris, | |
| 160 isDeferred: deferredKeyword != null, | |
| 161 annotations: metadata, | |
| 162 )); | |
| 163 if (uri == 'dart:core') isDartCoreImported = true; | |
| 164 checkEmpty(); | |
| 165 } | |
| 166 | |
| 167 void endPart(Token partKeyword, Token semicolon) { | |
| 168 debugEvent("Part"); | |
| 169 String uri = pop(); | |
| 170 // ignore: strong_mode_down_cast_composite | |
| 171 List<UnlinkedExpr> metadata = pop(); | |
| 172 topScope.unit.parts.add(new UnlinkedPartBuilder(annotations: metadata)); | |
| 173 topScope.publicNamespace.parts.add(uri); | |
| 174 checkEmpty(); | |
| 175 } | |
| 176 | |
| 177 void endLibraryName(Token libraryKeyword, Token semicolon) { | |
| 178 debugEvent("endLibraryName"); | |
| 179 String name = pop(); | |
| 180 // ignore: strong_mode_down_cast_composite | |
| 181 List<UnlinkedExpr> metadata = pop(); // metadata | |
| 182 | |
| 183 topScope.unit.libraryName = name; | |
| 184 topScope.unit.libraryAnnotations = metadata; | |
| 185 if (name == 'dart.core') isCoreLibrary = true; | |
| 186 } | |
| 187 | |
| 188 void endPartOf(Token partKeyword, Token semicolon) { | |
| 189 debugEvent("endPartOf"); | |
| 190 String name = pop(); | |
| 191 pop(); // metadata | |
| 192 topScope.unit.isPartOf = true; | |
| 193 if (name == 'dart.core') isCoreLibrary = true; | |
| 194 } | |
| 195 | |
| 196 // classes, enums, mixins, and typedefs. | |
| 197 | |
| 198 void beginClassDeclaration(Token beginToken, Token name) { | |
| 199 debugEvent("beginClass"); | |
| 200 var classScope = scope = new ClassScope(scope); | |
| 201 classScope.className = name.value; | |
| 202 } | |
| 203 | |
| 204 void endClassBody(int memberCount, Token beginToken, Token endToken) { | |
| 205 debugEvent("ClassBody"); | |
| 206 } | |
| 207 | |
| 208 void endClassDeclaration(int interfacesCount, Token beginToken, Token classKey word, | |
| 209 Token extendsKeyword, Token implementsKeyword, Token endToken) { | |
| 210 debugEvent("endClassDeclaration"); | |
| 211 // ignore: strong_mode_down_cast_composite | |
| 212 List<EntityRefBuilder> interfaces = popList(interfacesCount); | |
| 213 EntityRef supertype = pop(); | |
| 214 // ignore: strong_mode_down_cast_composite | |
| 215 List<UnlinkedTypeParamBuilder> typeVariables = pop(); | |
| 216 String name = pop(); | |
| 217 int modifiers = pop(); | |
| 218 List metadata = pop(); | |
| 219 checkEmpty(); | |
| 220 | |
| 221 ClassScope s = scope; | |
| 222 s.className = name; | |
| 223 s.currentClass | |
| 224 ..name = name | |
| 225 ..isAbstract = modifiers & _abstract_flag != 0 | |
| 226 // ignore: strong_mode_down_cast_composite | |
| 227 ..annotations = metadata | |
| 228 ..typeParameters = typeVariables | |
| 229 ..interfaces = interfaces; | |
| 230 if (supertype != null) { | |
| 231 s.currentClass.supertype = supertype; | |
| 232 } else { | |
| 233 s.currentClass.hasNoSupertype = isCoreLibrary && name == 'Object'; | |
| 234 } | |
| 235 scope = scope.parent; | |
| 236 topScope.unit.classes.add(s.currentClass); | |
| 237 if (_isPrivate(name)) return; | |
| 238 s.publicName | |
| 239 ..name = name | |
| 240 ..kind = ReferenceKind.classOrEnum | |
| 241 ..numTypeParameters = typeVariables?.length; | |
| 242 topScope.publicNamespace.names.add(s.publicName); | |
| 243 } | |
| 244 | |
| 245 void beginEnum(Token token) { | |
| 246 debugEvent("beginEnum"); | |
| 247 scope = new EnumScope(scope); | |
| 248 } | |
| 249 | |
| 250 void endEnum(Token enumKeyword, Token endBrace, int count) { | |
| 251 debugEvent("Enum"); | |
| 252 // ignore: strong_mode_down_cast_composite | |
| 253 List<String> constants = popList(count); | |
| 254 String name = pop(); | |
| 255 List metadata = pop(); | |
| 256 checkEmpty(); | |
| 257 EnumScope s = scope; | |
| 258 scope = s.parent; | |
| 259 s.currentEnum | |
| 260 ..name = name | |
| 261 // ignore: strong_mode_down_cast_composite | |
| 262 ..annotations = metadata; | |
| 263 s.top.unit.enums.add(s.currentEnum); | |
| 264 | |
| 265 // public namespace: | |
| 266 var e = new UnlinkedPublicNameBuilder( | |
| 267 name: name, | |
| 268 kind: ReferenceKind.classOrEnum, | |
| 269 numTypeParameters: 0); | |
| 270 for (var s in constants) { | |
| 271 e.members.add(new UnlinkedPublicNameBuilder( | |
| 272 name: s, | |
| 273 kind: ReferenceKind.propertyAccessor, | |
| 274 numTypeParameters: 0)); | |
| 275 } | |
| 276 topScope.publicNamespace.names.add(e); | |
| 277 } | |
| 278 | |
| 279 void endMixinApplication(Token withKeyword) { | |
| 280 debugEvent("MixinApplication"); | |
| 281 ClassScope s = scope; | |
| 282 // ignore: strong_mode_down_cast_composite | |
| 283 s.currentClass.mixins = pop(); | |
| 284 } | |
| 285 | |
| 286 void beginNamedMixinApplication(Token beginToken, Token name) { | |
| 287 debugEvent('beginNamedMixinApplication'); | |
| 288 scope = new ClassScope(scope); | |
| 289 } | |
| 290 | |
| 291 void endNamedMixinApplication( | |
| 292 Token begin, Token classKeyword, Token equals, | |
| 293 Token implementsKeyword, Token endToken) { | |
| 294 debugEvent("endNamedMixinApplication"); | |
| 295 // ignore: strong_mode_down_cast_composite | |
| 296 List<EntityRef> interfaces = popIfNotNull(implementsKeyword); | |
| 297 EntityRef supertype = pop(); | |
| 298 List typeVariables = pop(); | |
| 299 String name = pop(); | |
| 300 int modifiers = pop(); | |
| 301 List metadata = pop(); | |
| 302 // print('TODO: end mix, $name'); | |
| 303 checkEmpty(); | |
| 304 | |
| 305 ClassScope s = scope; | |
| 306 s.currentClass | |
| 307 ..name = name | |
| 308 ..isAbstract = modifiers & _abstract_flag != 0 | |
| 309 ..isMixinApplication = true | |
| 310 // ignore: strong_mode_down_cast_composite | |
| 311 ..annotations = metadata | |
| 312 // ignore: strong_mode_down_cast_composite | |
| 313 ..typeParameters = typeVariables | |
| 314 ..interfaces = interfaces; | |
| 315 if (supertype != null) { | |
| 316 s.currentClass.supertype = supertype; | |
| 317 } else { | |
| 318 s.currentClass.hasNoSupertype = isCoreLibrary && name == 'Object'; | |
| 319 } | |
| 320 scope = scope.parent; | |
| 321 topScope.unit.classes.add(s.currentClass); | |
| 322 | |
| 323 _addNameIfPublic(name, ReferenceKind.classOrEnum, typeVariables.length); | |
| 324 } | |
| 325 | |
| 326 void beginFunctionTypeAlias(Token token) { | |
| 327 debugEvent('beginFunctionTypeAlias'); | |
| 328 // TODO: use a single scope | |
| 329 scope = new TypeParameterScope(scope); | |
| 330 } | |
| 331 | |
| 332 void endFunctionTypeAlias(Token typedefKeyword, Token equals, Token endToken) { | |
| 333 debugEvent("endFunctionTypeAlias"); | |
| 334 List formals = pop(); | |
| 335 List typeVariables = pop(); | |
| 336 String name = pop(); | |
| 337 EntityRef returnType = pop(); | |
| 338 List metadata = pop(); | |
| 339 // print('TODO: type alias $name'); | |
| 340 checkEmpty(); | |
| 341 | |
| 342 scope = scope.parent; | |
| 343 topScope.unit.typedefs.add(new UnlinkedTypedefBuilder( | |
| 344 name: name, | |
| 345 // ignore: strong_mode_down_cast_composite | |
| 346 typeParameters: typeVariables, | |
| 347 returnType: returnType, | |
| 348 // ignore: strong_mode_down_cast_composite | |
| 349 parameters: formals, | |
| 350 // ignore: strong_mode_down_cast_composite | |
| 351 annotations: metadata)); | |
| 352 | |
| 353 _addNameIfPublic(name, ReferenceKind.typedef, typeVariables.length); | |
| 354 } | |
| 355 | |
| 356 // members: fields, methods. | |
| 357 | |
| 358 void beginTopLevelMember(Token token) { | |
| 359 typeSeen = false; | |
| 360 inConstContext = false; | |
| 361 } | |
| 362 | |
| 363 void beginMember(Token token) { | |
| 364 typeSeen = false; | |
| 365 inConstContext = false; | |
| 366 } | |
| 367 | |
| 368 void endMember() { | |
| 369 debugEvent("Member"); | |
| 370 } | |
| 371 | |
| 372 void handleType(Token beginToken, Token endToken) { | |
| 373 debugEvent("Type"); | |
| 374 // ignore: strong_mode_down_cast_composite | |
| 375 List<EntityRef> arguments = pop(); | |
| 376 String name = pop(); | |
| 377 | |
| 378 var type; | |
| 379 if (name.contains('.')) { | |
| 380 var parts = name.split('.'); | |
| 381 for (var p in parts) { | |
| 382 type = type == null | |
| 383 ? new LazyEntityRef(p, scope) | |
| 384 : new NestedLazyEntityRef(type, p, scope); | |
| 385 } | |
| 386 } else { | |
| 387 type = new LazyEntityRef(name, scope); | |
| 388 } | |
| 389 type.typeArguments = arguments; | |
| 390 push(type); | |
| 391 typeSeen = true; | |
| 392 } | |
| 393 | |
| 394 void endTypeList(int count) { | |
| 395 debugEvent("TypeList"); | |
| 396 push(popList(count) ?? NullValue.TypeList); | |
| 397 } | |
| 398 | |
| 399 static int parsed = 0; | |
| 400 static int total = 0; | |
| 401 beginInitializer(Token token) { | |
| 402 // TODO(paulberry): Add support for this. | |
| 403 } | |
| 404 | |
| 405 beginFieldInitializer(Token token) { | |
| 406 // TODO(paulberry): Copied from beginFieldInitializer. Is all of this neede d? | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:13
did you mean from beginInitializer?
 
Paul Berry
2017/03/09 23:26:23
Oops, yes.  I've removed this TODO--it was a place
 
 | |
| 407 debugEvent("beginFieldInitializer"); | |
| 408 total++; | |
| 409 if (needInitializer) { | |
| 410 parsed++; | |
| 411 if (inConstContext) { | |
| 412 push(constBuilder.computeExpression(token.next, scope)); | |
| 413 } else { | |
| 414 push(initializerBuilder.computeExpression(token.next, scope)); | |
| 415 } | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 void endInitializer(Token assignmentOperator) { | |
| 420 // TODO(paulberry): add support for this. | |
| 421 debugEvent("Initializer $typeSeen $assignmentOperator"); | |
| 422 } | |
| 423 | |
| 424 void endFieldInitializer(Token assignmentOperator) { | |
| 425 // TODO(paulberry): copied from endInitializer. Is all of this needed? | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
just to make sure I follow - you are wondering if
 
Paul Berry
2017/03/09 23:26:23
At the time I wrote the TODO I didn't understand t
 
 | |
| 426 debugEvent("FieldInitializer $typeSeen $assignmentOperator"); | |
| 427 // This is a variable initializer and it's ignored for now. May also be | |
| 428 // constructor initializer. | |
| 429 var initializer = needInitializer && assignmentOperator != null | |
| 430 ? pop() : null; | |
| 431 var name = pop(); | |
| 432 push(new _InitializedName(name, new UnlinkedExecutableBuilder( | |
| 433 bodyExpr: initializer))); | |
| 434 } | |
| 435 | |
| 436 void endTopLevelFields(int count, Token beginToken, Token endToken) { | |
| 437 debugEvent("endTopLevelFields"); | |
| 438 _endFields(count, topScope.unit.variables, true); | |
| 439 checkEmpty(); | |
| 440 } | |
| 441 | |
| 442 void endFields(int count, Token covariantKeyword, Token beginToken, Token endT oken) { | |
| 443 debugEvent("Fields"); | |
| 444 var s = scope; | |
| 445 if (s is ClassScope) { | |
| 446 _endFields(count, s.currentClass.fields, false); | |
| 447 } else { | |
| 448 throw new UnimplementedError(); // TODO(paulberry): does this ever occur? | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
do you mean that we don't summarize enums altogeth
 
Paul Berry
2017/03/09 23:26:23
We do summarize enums, but the parsing of enums ne
 
 | |
| 449 // _endFields(count, s.currentEnum.values, false); | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 void _endFields(int count, List result, bool isTopLevel) { | |
| 454 debugEvent('EndFields: $count $isTopLevel'); | |
| 455 // ignore: strong_mode_down_cast_composite | |
| 456 List<_InitializedName> fields = popList(count); | |
| 457 EntityRef type = pop(); | |
| 458 int modifiers = pop(); | |
| 459 List metadata = pop(); | |
| 460 | |
| 461 bool isStatic = modifiers & _static_flag != 0; | |
| 462 bool isFinal = modifiers & _final_flag != 0; | |
| 463 bool isConst = modifiers & _const_flag != 0; | |
| 464 bool isInstance = !isStatic && !isTopLevel; | |
| 465 for (var field in fields) { | |
| 466 var name = field.name; | |
| 467 var initializer = field.initializer; | |
| 468 bool needsPropagatedType = initializer != null && (isFinal || isConst); | |
| 469 bool needsInferredType = | |
| 470 type == null && (initializer != null || isInstance); | |
| 471 result.add(new UnlinkedVariableBuilder( | |
| 472 isFinal: isFinal, | |
| 473 isConst: isConst, | |
| 474 isStatic: isStatic, | |
| 475 name: name, | |
| 476 type: type, | |
| 477 // ignore: strong_mode_down_cast_composite | |
| 478 annotations: metadata, | |
| 479 initializer: initializer, | |
| 480 propagatedTypeSlot: slotIf(needsPropagatedType), | |
| 481 inferredTypeSlot: slotIf(needsInferredType))); | |
| 482 | |
| 483 if (_isPrivate(name)) continue; | |
| 484 if (isTopLevel) { | |
| 485 _addPropertyName(name, includeSetter: !isFinal && !isConst); | |
| 486 } else if (isStatic) { | |
| 487 // Any reason setters are not added as well? | |
| 488 (scope as ClassScope).publicName.members.add(new UnlinkedPublicNameBuild er( | |
| 489 name: name, | |
| 490 kind: ReferenceKind.propertyAccessor, | |
| 491 numTypeParameters: 0)); | |
| 492 } | |
| 493 } | |
| 494 } | |
| 495 | |
| 496 | |
| 497 void endTopLevelMethod( | |
| 498 Token beginToken, Token getOrSet, Token endToken) { | |
| 499 debugEvent("endTopLevelMethod"); | |
| 500 int asyncModifier = pop(); | |
| 501 List formals = pop(); | |
| 502 List typeVariables = pop(); | |
| 503 String name = pop(); | |
| 504 EntityRef returnType = pop(); | |
| 505 int modifiers = pop(); | |
| 506 List metadata = pop(); | |
| 507 checkEmpty(); | |
| 508 | |
| 509 topScope.unit.executables.add(new UnlinkedExecutableBuilder( | |
| 510 name: getOrSet == 'set' ? '$name=' : name, | |
| 511 kind: getOrSet == 'get' | |
| 512 ? UnlinkedExecutableKind.getter | |
| 513 : (getOrSet == 'set' ? UnlinkedExecutableKind.setter | |
| 514 : UnlinkedExecutableKind.functionOrMethod), | |
| 515 isExternal: modifiers & _external_flag != 0, | |
| 516 isAbstract: modifiers & _abstract_flag != 0, | |
| 517 isAsynchronous: asyncModifier & _async_flag != 0, | |
| 518 isGenerator: asyncModifier & _star_flag != 0, | |
| 519 isStatic: modifiers & _static_flag != 0, | |
| 520 typeParameters: [], // TODO | |
| 521 returnType: returnType, | |
| 522 // ignore: strong_mode_down_cast_composite | |
| 523 parameters: formals, | |
| 524 // ignore: strong_mode_down_cast_composite | |
| 525 annotations: metadata, | |
| 526 inferredReturnTypeSlot: null, // not needed for top-levels | |
| 527 // skip body. | |
| 528 )); | |
| 529 | |
| 530 String normalizedName = getOrSet == 'set' ? '$name=' : name; | |
| 531 _addNameIfPublic( | |
| 532 normalizedName, | |
| 533 getOrSet != null ? ReferenceKind.topLevelPropertyAccessor : ReferenceKin d.topLevelFunction, | |
| 534 typeVariables?.length ?? 0 /* todo */); | |
| 535 } | |
| 536 | |
| 537 void endMethod(Token getOrSet, Token beginToken, Token endToken) { | |
| 538 debugEvent("Method"); | |
| 539 int asyncModifier = pop(); | |
| 540 // ignore: strong_mode_down_cast_composite | |
| 541 List<UnlinkedParam> formals = pop(); | |
| 542 // ignore: strong_mode_down_cast_composite | |
| 543 List<UnlinkedTypeParamBuilder> typeVariables = pop(); | |
| 544 String name = pop(); | |
| 545 EntityRef returnType = pop(); | |
| 546 int modifiers = pop(); | |
| 547 List metadata = pop(); | |
| 548 | |
| 549 ClassScope s = scope; | |
| 550 bool isStatic = modifiers & _static_flag != 0; | |
| 551 bool isConst = modifiers & _const_flag != 0; | |
| 552 bool isGetter = getOrSet == 'get'; | |
| 553 bool isSetter = getOrSet == 'set'; | |
| 554 bool isOperator = name == "operator"; // TODO | |
| 555 bool isConstructor = | |
| 556 name == s.className || name.startsWith('${s.className}.'); | |
| 557 | |
| 558 if (isConstructor) { | |
| 559 name = name == s.className ? '' : name.substring(name.indexOf('.') + 1); | |
| 560 } | |
| 561 | |
| 562 name = isSetter ? '$name=' : name; | |
| 563 // Note: we don't include bodies for any method. | |
| 564 s.currentClass.executables.add(new UnlinkedExecutableBuilder( | |
| 565 name: name, | |
| 566 kind: isGetter | |
| 567 ? UnlinkedExecutableKind.getter | |
| 568 : (isSetter | |
| 569 ? UnlinkedExecutableKind.setter | |
| 570 : (isConstructor | |
| 571 ? UnlinkedExecutableKind.constructor | |
| 572 : UnlinkedExecutableKind.functionOrMethod)), | |
| 573 isExternal: modifiers & _external_flag != 0, | |
| 574 isAbstract: modifiers & _abstract_flag != 0, | |
| 575 isAsynchronous: asyncModifier & _async_flag != 0, | |
| 576 isGenerator: asyncModifier & _star_flag != 0, | |
| 577 isStatic: isStatic, | |
| 578 isConst: isConst, | |
| 579 constCycleSlot: slotIf(isConst), | |
| 580 typeParameters: typeVariables, | |
| 581 returnType: returnType, | |
| 582 parameters: formals, // TODO: add inferred slot to args | |
| 583 // ignore: strong_mode_down_cast_composite | |
| 584 annotations: metadata, | |
| 585 inferredReturnTypeSlot: slotIf(returnType == null && !isStatic && | |
| 586 !isConstructor))); | |
| 587 | |
| 588 if (isConstructor && name == '') return; | |
| 589 if (_isPrivate(name)) return; | |
| 590 if (isSetter || isOperator) return; | |
| 591 if (!isStatic && !isConstructor) return; | |
| 592 s.publicName.members.add(new UnlinkedPublicNameBuilder( | |
| 593 name: name, | |
| 594 kind: isGetter ? ReferenceKind.propertyAccessor : | |
| 595 (isConstructor ? ReferenceKind.constructor : | |
| 596 ReferenceKind.method), | |
| 597 numTypeParameters: typeVariables.length)); | |
| 598 } | |
| 599 | |
| 600 void endTypeArguments(int count, Token beginToken, Token endToken) { | |
| 601 debugEvent("TypeArguments"); | |
| 602 push(popList(count) ?? const []); | |
| 603 } | |
| 604 | |
| 605 | |
| 606 void handleVoidKeyword(Token token) { | |
| 607 debugEvent("VoidKeyword"); | |
| 608 // TODO: skip the lazy mechanism | |
| 609 push(new LazyEntityRef("void", scope.top)); | |
| 610 } | |
| 611 | |
| 612 void endFormalParameter(Token covariantKeyword, Token thisKeyword, FormalParam eterType kind) { | |
| 613 debugEvent("FormalParameter"); | |
| 614 // TODO(sigmund): clean up? | |
| 615 var nameOrFormal = pop(); | |
| 616 if (nameOrFormal is String) { | |
| 617 EntityRef type = pop(); | |
| 618 pop(); // Modifiers | |
| 619 List metadata = pop(); | |
| 620 push(new UnlinkedParamBuilder( | |
| 621 name: nameOrFormal, | |
| 622 kind: _nextParamKind, | |
| 623 inheritsCovariantSlot: slotIf(type == null), | |
| 624 // ignore: strong_mode_down_cast_composite | |
| 625 annotations: metadata, | |
| 626 isInitializingFormal: thisKeyword != null, | |
| 627 type: type)); | |
| 628 } else { | |
| 629 push(nameOrFormal); | |
| 630 } | |
| 631 } | |
| 632 | |
| 633 // TODO(sigmund): handle metadata (this code is incomplete). | |
| 634 void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { | |
| 635 debugEvent("Metadata"); | |
| 636 List arguments = pop(); | |
| 637 var result = new UnlinkedExprBuilder(); | |
| 638 // If arguments are null, this is an expression, otherwise a constructor | |
| 639 // reference. | |
| 640 if (arguments == null) { | |
| 641 /* String postfix = */ popIfNotNull(periodBeforeName); | |
| 642 /* String expression = */ pop(); | |
| 643 //push([expression, postfix]); // @x or @p.x | |
| 644 } else { | |
| 645 /* String name = */ popIfNotNull(periodBeforeName); | |
| 646 // TODO(ahe): Type arguments are missing, eventually they should be | |
| 647 // available as part of [arguments]. | |
| 648 // List<String> typeArguments = null; | |
| 649 /* EntityRef typeName = */ pop(); | |
| 650 //push([typeName, typeArguments, name, arguments]); | |
| 651 } | |
| 652 push(result); | |
| 653 } | |
| 654 | |
| 655 void endMetadataStar(int count, bool forParameter) { | |
| 656 debugEvent("MetadataStar"); | |
| 657 push(popList(count) ?? NullValue.Metadata); | |
| 658 } | |
| 659 | |
| 660 void handleStringPart(token) { | |
| 661 debugEvent("handleStringPart"); | |
| 662 push(token.value.substring(1, token.value.length - 1)); | |
| 663 } | |
| 664 | |
| 665 void beginLiteralString(Token token) { | |
| 666 debugEvent("beginLiteralString"); | |
| 667 push(token.value.substring(1, token.value.length - 1)); | |
| 668 } | |
| 669 | |
| 670 void endLiteralString(int count) { | |
| 671 assert(count == 0); // TODO(sigmund): handle interpolation | |
| 672 } | |
| 673 | |
| 674 void handleQualified(Token period) { | |
| 675 debugEvent("handleQualified"); | |
| 676 String name = pop(); | |
| 677 String receiver = pop(); | |
| 678 push("$receiver.$name"); | |
| 679 } | |
| 680 | |
| 681 void endDottedName(count, firstIdentifier) { | |
| 682 push(popList(count).join('.')); | |
| 683 } | |
| 684 | |
| 685 void handleOperatorName(Token operatorKeyword, Token token) { | |
| 686 // TODO(sigmund): convert operator names to name used by summaries. | |
| 687 debugEvent("OperatorName"); | |
| 688 push(operatorKeyword.value); | |
| 689 } | |
| 690 | |
| 691 void endIdentifierList(int count) { | |
| 692 debugEvent("endIdentifierList"); | |
| 693 push(popList(count) ?? NullValue.IdentifierList); | |
| 694 } | |
| 695 | |
| 696 void handleModifier(Token token) { | |
| 697 debugEvent("Modifier"); | |
| 698 var modifier = _modifierFlag[token.stringValue]; | |
| 699 if (modifier & _const_flag != 0) inConstContext = true; | |
| 700 push(modifier); | |
| 701 } | |
| 702 | |
| 703 void handleModifiers(int count) { | |
| 704 debugEvent("Modifiers"); | |
| 705 push((popList(count) ?? const []).fold(0, (a, b) => a | b)); | |
| 706 } | |
| 707 | |
| 708 UnlinkedParamKind _nextParamKind; | |
| 709 void beginFormalParameters(Token begin) { | |
| 710 _nextParamKind = UnlinkedParamKind.required; | |
| 711 } | |
| 712 void beginOptionalFormalParameters(Token begin) { | |
| 713 _nextParamKind = | |
| 714 begin == '{' ? UnlinkedParamKind.named : UnlinkedParamKind.positional; | |
| 715 } | |
| 716 | |
| 717 void handleValuedFormalParameter(Token equals, Token token) { | |
| 718 debugEvent("ValuedFormalParameter"); | |
| 719 // TODO(sigmund): include default value on optional args. | |
| 720 } | |
| 721 | |
| 722 void endFunctionTypedFormalParameter(Token covariantKeyword, Token thisKeyword , FormalParameterType kind) { | |
| 723 debugEvent("FunctionTypedFormalParameter"); | |
| 724 // ignore: strong_mode_down_cast_composite | |
| 725 List<UnlinkedParamBuilder> formals = pop(); | |
| 726 if (formals != null) formals.forEach((p) => p.inheritsCovariantSlot = null); | |
| 727 | |
| 728 /* List typeVariables = */ pop(); | |
| 729 String name = pop(); | |
| 730 EntityRef returnType = pop(); | |
| 731 /* int modifiers = */ pop(); | |
| 732 List metadata = pop(); | |
| 733 | |
| 734 push(new UnlinkedParamBuilder( | |
| 735 name: name, | |
| 736 kind: _nextParamKind, | |
| 737 isFunctionTyped: true, | |
| 738 parameters: formals, | |
| 739 // ignore: strong_mode_down_cast_composite | |
| 740 annotations: metadata, | |
| 741 type: returnType)); | |
| 742 } | |
| 743 | |
| 744 void endOptionalFormalParameters( | |
| 745 int count, Token beginToken, Token endToken) { | |
| 746 debugEvent("OptionalFormalParameters"); | |
| 747 push(popList(count)); | |
| 748 } | |
| 749 | |
| 750 void endFormalParameters(int count, Token beginToken, Token endToken) { | |
| 751 debugEvent("FormalParameters"); | |
| 752 List formals = popList(count); | |
| 753 if (formals != null && formals.isNotEmpty) { | |
| 754 var last = formals.last; | |
| 755 if (last is List) { | |
| 756 var newList = new List(formals.length - 1 + last.length); | |
| 757 newList.setRange(0, formals.length - 1, formals); | |
| 758 newList.setRange(formals.length - 1, newList.length, last); | |
| 759 for (int i = 0; i < last.length; i++) { | |
| 760 newList[i + formals.length - 1] = last[i]; | |
| 761 } | |
| 762 formals = newList; | |
| 763 } | |
| 764 } | |
| 765 push(formals ?? NullValue.FormalParameters); | |
| 766 } | |
| 767 | |
| 768 void endTypeVariables(int count, Token beginToken, Token endToken) { | |
| 769 debugEvent("TypeVariables"); | |
| 770 push(popList(count) ?? const []); | |
| 771 } | |
| 772 | |
| 773 void endTypeVariable(Token token, Token extendsOrSuper) { | |
| 774 debugEvent("endTypeVariable"); | |
| 775 EntityRef bound = pop(); | |
| 776 String name = pop(); | |
| 777 | |
| 778 var s = scope; | |
| 779 if (s is TypeParameterScope) { | |
| 780 s.typeParameters.add(name); | |
| 781 } else { | |
| 782 throw new UnimplementedError(); // TODO(paulberry) | |
| 783 } | |
| 784 push(new UnlinkedTypeParamBuilder( | |
| 785 name: name, | |
| 786 bound: bound)); | |
| 787 } | |
| 788 | |
| 789 void endFactoryMethod(Token beginToken, Token endToken) { | |
| 790 debugEvent("FactoryMethod"); | |
| 791 throw new UnimplementedError(); // TODO(paulberry) | |
| 792 // pop(); // async-modifiers | |
| 793 // /* List<FormalParameterBuilder> formals = */ pop(); | |
| 794 // var name = pop(); | |
| 795 // /* List<MetadataBuilder> metadata = */ pop(); | |
| 796 } | |
| 797 | |
| 798 | |
| 799 void endRedirectingFactoryBody(Token beginToken, Token endToken) { | |
| 800 debugEvent("RedirectingFactoryBody"); | |
| 801 pop(); // Discard ConstructorReferenceBuilder. | |
| 802 } | |
| 803 | |
| 804 void endConstructorReference( | |
| 805 Token start, Token periodBeforeName, Token endToken) { | |
| 806 var ctorName = popIfNotNull(periodBeforeName); | |
| 807 var typeArguments = pop(); | |
| 808 var className = pop(); | |
| 809 push(['ctor-ref:', className, typeArguments, ctorName]); | |
| 810 } | |
| 811 | |
| 812 void endInitializers(int count, Token beginToken, Token endToken) { | |
| 813 debugEvent("Initializers"); | |
| 814 // TODO(sigmund): include const-constructor initializers | |
| 815 } | |
| 816 | |
| 817 void handleNoFieldInitializer(Token token) { | |
| 818 debugEvent("NoFieldInitializer"); | |
| 819 push(new _InitializedName(pop(), null)); | |
| 820 } | |
| 821 | |
| 822 void handleNoTypeVariables(Token token) { | |
| 823 debugEvent("NoTypeVariables"); | |
| 824 push(const []); | |
| 825 } | |
| 826 | |
| 827 void handleNoInitializers() { | |
| 828 debugEvent("NoInitializers"); | |
| 829 // This is a constructor initializer and it's ignored for now. | |
| 830 } | |
| 831 | |
| 832 void handleNoFunctionBody(Token token) { | |
| 833 debugEvent("NoFunctionBody"); | |
| 834 // Ignored for now. We shouldn't see any function bodies. | |
| 835 } | |
| 836 | |
| 837 void handleAsyncModifier(Token asyncToken, Token starToken) { | |
| 838 debugEvent("AsyncModifier"); | |
| 839 int asyncModifier = 0; | |
| 840 if (asyncToken == "async") asyncModifier |= _async_flag; | |
| 841 if (asyncToken == "sync") asyncModifier |= _sync_flag; | |
| 842 if (starToken != null) asyncModifier |= _star_flag; | |
| 843 push(asyncModifier); | |
| 844 } | |
| 845 | |
| 846 // helpers to work with the summary format. | |
| 847 | |
| 848 | |
| 849 /// Assign the next slot. | |
| 850 int assignSlot() => ++_slots; | |
| 851 | |
| 852 /// Assign the next slot if [condition] is true. | |
| 853 int slotIf(bool condition) => condition ? assignSlot() : 0; | |
| 854 | |
| 855 /// Whether a name is private and should be excluded from the public | |
| 856 /// namespace. | |
| 857 bool _isPrivate(String name) => name.startsWith('_'); | |
| 858 | |
| 859 /// Add [name] to the public namespace if it's public. | |
| 860 void _addNameIfPublic( | |
| 861 String name, ReferenceKind kind, int numTypeParameters) { | |
| 862 if (_isPrivate(name)) return null; | |
| 863 _addName(name, kind, numTypeParameters: numTypeParameters); | |
| 864 } | |
| 865 | |
| 866 /// Add [name] to the public namespace. | |
| 867 void _addName( | |
| 868 String name, ReferenceKind kind, {int numTypeParameters: 0}) { | |
| 869 topScope.publicNamespace.names.add(new UnlinkedPublicNameBuilder( | |
| 870 name: name, | |
| 871 kind: kind, | |
| 872 numTypeParameters: numTypeParameters)); | |
| 873 } | |
| 874 | |
| 875 /// Add `name` and, if requested, `name=` to the public namespace. | |
| 876 void _addPropertyName(String name, {bool includeSetter: false}) { | |
| 877 _addName(name, ReferenceKind.topLevelPropertyAccessor); | |
| 878 if (includeSetter) { | |
| 879 _addName('$name=', ReferenceKind.topLevelPropertyAccessor); | |
| 880 } | |
| 881 } | |
| 882 | |
| 883 /// If enabled, show a debug message. | |
| 884 void debugEvent(String name) { | |
| 885 if (const bool.fromEnvironment('DEBUG', defaultValue: false)) { | |
| 886 var s = stack.join(' :: '); | |
| 887 if (s == '') s = '<empty>'; | |
| 888 var bits = 'type?: $typeSeen, const?: $inConstContext'; | |
| 889 var prefix = "do $name on:"; | |
| 890 prefix = '$prefix${" " * (30 - prefix.length)}'; | |
| 891 print('$prefix $bits $s'); | |
| 892 } | |
| 893 } | |
| 894 | |
| 895 void handleFormalParameterWithoutValue(Token token) { | |
| 896 debugEvent("FormalParameterWithoutValue"); | |
| 897 } | |
| 898 } | |
| 899 | |
| 900 /// Internal representation of an initialized name. | |
| 901 class _InitializedName { | |
| 902 final String name; | |
| 903 final UnlinkedExecutableBuilder initializer; | |
| 904 _InitializedName(this.name, this.initializer); | |
| 905 | |
| 906 toString() => "II:" + (initializer != null ? "$name = $initializer" : name); | |
| 907 } | |
| 908 | |
| 909 /// Parser listener to build simplified AST expresions. | |
| 910 /// | |
| 911 /// The parser produces different trees depending on whether it is used for | |
| 912 /// constants or initializers, so subclasses specialize the logic accordingly. | |
| 913 abstract class ExpressionListener extends StackListener { | |
| 914 // Underlying parser that invokes this listener. | |
| 915 Parser get parser; | |
| 916 | |
| 917 /// Whether to ignore the next reduction. Used to ignore nested expresions | |
| 918 /// that are either invalid (in constants) or unnecessary (for initializers). | |
| 919 bool get ignore => _withinFunction > 0 || _withinCascades > 0; | |
| 920 | |
| 921 /// Whether this listener is used to build const expressions. | |
| 922 bool get forConst => false; | |
| 923 | |
| 924 void push(Object o); | |
| 925 | |
| 926 UnlinkedExprBuilder computeExpression(Token token, Scope scope) { | |
| 927 debugStart(token); | |
| 928 parser.parseExpression(token); | |
| 929 debugEvent('---- END ---'); | |
| 930 Expression node = pop(); | |
| 931 checkEmpty(); | |
| 932 return new Serializer(scope, forConst).run(node); | |
| 933 } | |
| 934 | |
| 935 void handleNoInitializer() {} | |
| 936 | |
| 937 void handleIdentifier(Token token, IdentifierContext context) { | |
| 938 debugEvent("Identifier"); | |
| 939 if (ignore) return; | |
| 940 push(new Ref(token.value)); | |
| 941 } | |
| 942 | |
| 943 | |
| 944 void endFormalParameter(Token covariantKeyword, Token thisKeyword, FormalParam eterType kind) { | |
| 945 debugEvent("FormalParameter"); | |
| 946 assert(ignore); | |
| 947 } | |
| 948 | |
| 949 void endFunctionBody(int count, Token begin, Token end) { | |
| 950 debugEvent("FunctionBody"); | |
| 951 assert(ignore); | |
| 952 } | |
| 953 | |
| 954 void endFunctionName(Token token) { | |
| 955 debugEvent("FunctionName"); | |
| 956 assert(ignore); | |
| 957 } | |
| 958 | |
| 959 void endFormalParameters(int c, begin, end) { | |
| 960 debugEvent("FormalParameters"); | |
| 961 assert(ignore); | |
| 962 } | |
| 963 | |
| 964 void handleAsyncModifier(Token asyncToken, Token starToken) { | |
| 965 debugEvent("AsyncModifier"); | |
| 966 assert(ignore); | |
| 967 } | |
| 968 | |
| 969 void endReturnStatement(hasValue, Token begin, Token end) { | |
| 970 debugEvent("ReturnStatement"); | |
| 971 assert(ignore); | |
| 972 } | |
| 973 | |
| 974 void handleStringPart(token) { | |
| 975 debugEvent("handleStringPart"); | |
| 976 if (ignore) return; | |
| 977 push(new StringLiteral(token.value)); | |
| 978 } | |
| 979 | |
| 980 void beginLiteralString(Token token) { | |
| 981 debugEvent("beginLiteralString"); | |
| 982 if (ignore) return; | |
| 983 push(new StringLiteral(token.value)); | |
| 984 } | |
| 985 | |
| 986 void handleStringJuxtaposition(int count) { | |
| 987 debugEvent("StringJuxtaposition"); | |
| 988 if (ignore) return; | |
| 989 popList(count); | |
| 990 push(new StringLiteral('<juxtapose $count>')); | |
| 991 } | |
| 992 | |
| 993 void endLiteralString(int interpolationCount) { | |
| 994 debugEvent("endLiteralString"); | |
| 995 if (interpolationCount != 0) { | |
| 996 popList(2 * interpolationCount + 1); | |
| 997 push(new StringLiteral("<interpolate $interpolationCount>")); | |
| 998 } | |
| 999 } | |
| 1000 | |
| 1001 void endThrowExpression(throwToken, token) { | |
| 1002 debugEvent("Throw"); | |
| 1003 assert(ignore); | |
| 1004 } | |
| 1005 | |
| 1006 void handleNoType(Token token) { | |
| 1007 debugEvent("NoType"); | |
| 1008 if (ignore) return; | |
| 1009 push(NullValue.Type); | |
| 1010 } | |
| 1011 | |
| 1012 void handleNoFormalParameters(Token token) { | |
| 1013 debugEvent("NoFormalParameters"); | |
| 1014 assert(ignore); | |
| 1015 } | |
| 1016 | |
| 1017 void handleNoArguments(Token token) { | |
| 1018 debugEvent("NoArguments"); | |
| 1019 if (ignore) return; | |
| 1020 var typeArguments = pop(); | |
| 1021 assert(typeArguments == null); | |
| 1022 push(NullValue.Arguments); | |
| 1023 } | |
| 1024 | |
| 1025 void handleNoFunctionBody(Token token) { | |
| 1026 debugEvent("NoFunctionBody"); | |
| 1027 assert(ignore); | |
| 1028 } | |
| 1029 | |
| 1030 void handleNoInitializers() { | |
| 1031 debugEvent("NoInitializers"); | |
| 1032 assert(ignore); | |
| 1033 } | |
| 1034 | |
| 1035 void handleNoTypeArguments(Token token) { | |
| 1036 debugEvent("NoTypeArguments"); | |
| 1037 if (ignore) return; | |
| 1038 push(NullValue.TypeArguments); | |
| 1039 } | |
| 1040 | |
| 1041 // type-arguments are expected to be type references passed to constructors | |
| 1042 // and generic methods, we need them to model instantiations. | |
| 1043 void endTypeArguments(int count, Token beginToken, Token endToken) { | |
| 1044 debugEvent("TypeArguments"); | |
| 1045 if (ignore) return; | |
| 1046 push(popList(count) ?? const <TypeRef>[]); | |
| 1047 } | |
| 1048 | |
| 1049 void handleVoidKeyword(Token token) { | |
| 1050 debugEvent("VoidKeyword"); | |
| 1051 assert(ignore); | |
| 1052 } | |
| 1053 | |
| 1054 void handleType(Token beginToken, Token endToken) { | |
| 1055 debugEvent("Type"); | |
| 1056 if (ignore) return; | |
| 1057 // ignore: strong_mode_down_cast_composite | |
| 1058 List<TypeRef> arguments = pop(); | |
| 1059 Ref name = pop(); | |
| 1060 push(new TypeRef(name, arguments)); | |
| 1061 } | |
| 1062 | |
| 1063 void endTypeList(int count) { | |
| 1064 debugEvent("TypeList"); | |
| 1065 push(popList(count) ?? const <TypeRef>[]); | |
| 1066 } | |
| 1067 | |
| 1068 void handleBinaryExpression(Token operator) { | |
| 1069 debugEvent("BinaryExpression"); | |
| 1070 if (ignore) return; | |
| 1071 Expression right = pop(); | |
| 1072 Expression left = pop(); | |
| 1073 var kind = operator.kind; | |
| 1074 if (kind == PERIOD_TOKEN) { | |
| 1075 if (left is Ref && right is Ref && right.prefix == null && left.prefixDept h < 2) { | |
| 1076 push(new Ref(right.name, left)); | |
| 1077 return; | |
| 1078 } | |
| 1079 if (right is Ref) { | |
| 1080 push(new Load(left, right.name)); | |
| 1081 return; | |
| 1082 } | |
| 1083 } | |
| 1084 push(new Binary(left, right, kind)); | |
| 1085 } | |
| 1086 | |
| 1087 void handleUnaryPrefixExpression(Token operator) { | |
| 1088 debugEvent("UnaryPrefix"); | |
| 1089 if (ignore) return; | |
| 1090 push(new Unary(pop(), operator.kind)); | |
| 1091 } | |
| 1092 | |
| 1093 void handleLiteralNull(Token token) { | |
| 1094 debugEvent("LiteralNull"); | |
| 1095 if (ignore) return; | |
| 1096 push(new NullLiteral()); | |
| 1097 } | |
| 1098 | |
| 1099 void handleConditionalExpression(Token question, Token colon) { | |
| 1100 debugEvent("ConditionalExpression"); | |
| 1101 if (ignore) return; | |
| 1102 var falseBranch = pop(); | |
| 1103 var trueBranch = pop(); | |
| 1104 var cond = pop(); | |
| 1105 push(new Conditional(cond, trueBranch, falseBranch)); | |
| 1106 } | |
| 1107 | |
| 1108 void handleLiteralInt(Token token) { | |
| 1109 debugEvent("LiteralInt"); | |
| 1110 if (ignore) return; | |
| 1111 push(new IntLiteral(int.parse(token.value))); | |
| 1112 } | |
| 1113 | |
| 1114 void handleLiteralDouble(Token token) { | |
| 1115 debugEvent("LiteralDouble"); | |
| 1116 if (ignore) return; | |
| 1117 push(new DoubleLiteral(double.parse(token.value))); | |
| 1118 } | |
| 1119 | |
| 1120 void handleLiteralBool(Token token) { | |
| 1121 debugEvent("LiteralBool"); | |
| 1122 if (ignore) return; | |
| 1123 push(new BoolLiteral(token.value == 'true')); | |
| 1124 } | |
| 1125 | |
| 1126 void handleIsOperator(Token operator, Token not, Token endToken) { | |
| 1127 debugEvent("Is"); | |
| 1128 if (ignore) return; | |
| 1129 push(new Is(pop(), pop())); | |
| 1130 } | |
| 1131 | |
| 1132 void handleConstExpression(Token token) { | |
| 1133 debugEvent("ConstExpression"); | |
| 1134 if (ignore) return; | |
| 1135 List args = pop(); | |
| 1136 var constructorName = pop(); | |
| 1137 var positional = args.where((a) => a is! NamedArg).toList(); | |
| 1138 var named = args.where((a) => a is NamedArg).toList(); | |
| 1139 // ignore: strong_mode_down_cast_composite | |
| 1140 push(new ConstCreation(constructorName, positional, named)); | |
| 1141 } | |
| 1142 | |
| 1143 void handleLiteralList(count, begin, constKeyword, end) { | |
| 1144 debugEvent("LiteralList"); | |
| 1145 if (ignore) return; | |
| 1146 var values = popList(count) ?? const <Expression>[]; | |
| 1147 // ignore: strong_mode_down_cast_composite | |
| 1148 List<TypeRef> typeArguments = pop(); | |
| 1149 var type = typeArguments?.single; | |
| 1150 // ignore: strong_mode_down_cast_composite | |
| 1151 push(new ListLiteral(type, values, constKeyword != null)); | |
| 1152 } | |
| 1153 | |
| 1154 void endLiteralMapEntry(colon, token) { | |
| 1155 debugEvent('MapEntry'); | |
| 1156 if (ignore) return; | |
| 1157 var value = pop(); | |
| 1158 var key = pop(); | |
| 1159 push(new KeyValuePair(key, value)); | |
| 1160 } | |
| 1161 | |
| 1162 void handleLiteralMap(count, begin, constKeyword, end) { | |
| 1163 debugEvent('LiteralMap'); | |
| 1164 if (ignore) return; | |
| 1165 var values = popList(count) ?? const <KeyValuePair>[]; | |
| 1166 var typeArgs = pop() ?? const <TypeRef>[]; | |
| 1167 // ignore: strong_mode_down_cast_composite | |
| 1168 push(new MapLiteral(typeArgs, values, constKeyword != null)); | |
| 1169 } | |
| 1170 | |
| 1171 void endLiteralSymbol(token, int dots) { | |
| 1172 debugEvent('LiteralSymbol'); | |
| 1173 if (ignore) return; | |
| 1174 push(new SymbolLiteral(popList(dots).join('.'))); | |
| 1175 } | |
| 1176 | |
| 1177 void handleQualified(period) { | |
| 1178 debugEvent('Qualified'); | |
| 1179 if (ignore) return; | |
| 1180 Ref name = pop(); | |
| 1181 Ref prefix = pop(); | |
| 1182 assert(name.prefix == null); | |
| 1183 assert(prefix.prefix == null); | |
| 1184 push(new Ref(name.name, prefix)); | |
| 1185 } | |
| 1186 | |
| 1187 int _withinFunction = 0; | |
| 1188 void beginFunctionDeclaration(token) { | |
| 1189 debugEvent("BeginFunctionDeclaration"); | |
| 1190 _withinFunction++; | |
| 1191 } | |
| 1192 | |
| 1193 // TODO(sigmund): remove | |
| 1194 static const _invariantCheckToken = "invariant check: starting a function"; | |
| 1195 // type-variables are the declared parameters on declarations. | |
| 1196 void handleNoTypeVariables(Token token) { | |
| 1197 debugEvent("NoTypeVariables"); | |
| 1198 if (ignore) return; | |
| 1199 push(_invariantCheckToken); | |
| 1200 } | |
| 1201 | |
| 1202 void endTypeVariable(Token token, Token extendsOrSuper) { | |
| 1203 debugEvent("endTypeVariable"); | |
| 1204 assert(ignore); | |
| 1205 } | |
| 1206 | |
| 1207 void endTypeVariables(int count, Token beginToken, Token endToken) { | |
| 1208 debugEvent("TypeVariables"); | |
| 1209 assert(ignore); | |
| 1210 } | |
| 1211 | |
| 1212 void beginUnnamedFunction(token) { | |
| 1213 debugEvent("BeginUnnamedFunction"); | |
| 1214 var check = pop(); | |
| 1215 assert(check == _invariantCheckToken); | |
| 1216 _withinFunction++; | |
| 1217 } | |
| 1218 | |
| 1219 void _endFunction(); | |
| 1220 void endFunctionDeclaration(token) { | |
| 1221 debugEvent("FunctionDeclaration"); | |
| 1222 _withinFunction--; | |
| 1223 if (ignore) return; | |
| 1224 _endFunction(); | |
| 1225 } | |
| 1226 void endUnnamedFunction(token) { | |
| 1227 debugEvent("UnnamedFunction"); | |
| 1228 _withinFunction--; | |
| 1229 if (ignore) return; | |
| 1230 _endFunction(); | |
| 1231 } | |
| 1232 | |
| 1233 int _withinCascades = 0; | |
| 1234 void beginCascade(Token token) { | |
| 1235 _withinCascades++; | |
| 1236 } | |
| 1237 | |
| 1238 void endCascade() { | |
| 1239 _withinCascades--; | |
| 1240 throw new UnimplementedError(); // TODO(paulberry): fix the code below. | |
| 1241 // _endCascade(); | |
| 1242 } | |
| 1243 | |
| 1244 void endSend(Token beginToken, Token endToken) { | |
| 1245 debugEvent("EndSend"); | |
| 1246 if (ignore) return; | |
| 1247 // ignore: strong_mode_down_cast_composite | |
| 1248 List<Expression> args = pop(); | |
| 1249 if (args != null) { | |
| 1250 /* var typeArgs = */ pop(); | |
| 1251 var receiver = pop(); | |
| 1252 // TODO(sigmund): consider making identical a binary operator. | |
| 1253 if (receiver is Ref && receiver.name == 'identical') { | |
| 1254 assert(receiver.prefix == null); | |
| 1255 assert(args.length == 2); | |
| 1256 push(new Identical(args[0], args[1])); | |
| 1257 return; | |
| 1258 } | |
| 1259 _unhandledSend(); | |
| 1260 } | |
| 1261 } | |
| 1262 | |
| 1263 void _unhandledSend(); | |
| 1264 | |
| 1265 void endConstructorReference( | |
| 1266 Token start, Token periodBeforeName, Token endToken) { | |
| 1267 debugEvent("ConstructorReference $start $periodBeforeName"); | |
| 1268 Ref ctorName = popIfNotNull(periodBeforeName); | |
| 1269 assert(ctorName?.prefix == null); | |
| 1270 // ignore: strong_mode_down_cast_composite | |
| 1271 List<TypeRef> typeArgs = pop(); | |
| 1272 Ref type = pop(); | |
| 1273 push(new ConstructorName( | |
| 1274 new TypeRef(type, typeArgs), ctorName?.name)); | |
| 1275 } | |
| 1276 | |
| 1277 void handleModifier(Token token) { | |
| 1278 debugEvent("Modifier"); | |
| 1279 assert(ignore); | |
| 1280 } | |
| 1281 | |
| 1282 void handleModifiers(int count) { | |
| 1283 debugEvent("Modifiers"); | |
| 1284 assert(ignore); | |
| 1285 } | |
| 1286 | |
| 1287 /// Overriden: the base class throws when something is not handled, we avoid | |
| 1288 /// implementing a few handlers when we know we can ignore them. | |
| 1289 @override | |
| 1290 void logEvent(e) { | |
| 1291 if (ignore) return; | |
| 1292 super.logEvent(e); | |
| 1293 } | |
| 1294 | |
| 1295 // debug helpers | |
| 1296 | |
| 1297 void debugEvent(String name) { | |
| 1298 if (const bool.fromEnvironment('CDEBUG', defaultValue: false)) { | |
| 1299 var s = stack.join(' :: '); | |
| 1300 if (s == '') s = '<empty>'; | |
| 1301 var bits = '$_withinFunction,$_withinCascades'; | |
| 1302 var prefix = ignore ? "ignore $name on:" : "do $name on:"; | |
| 1303 prefix = '$prefix${" " * (30 - prefix.length)}'; | |
| 1304 print('$prefix $bits $s'); | |
| 1305 } | |
| 1306 } | |
| 1307 | |
| 1308 void debugStart(Token token) { | |
| 1309 debugEvent('\n---- START: $runtimeType ---'); | |
| 1310 if (const bool.fromEnvironment('CDEBUG', defaultValue: false)) { | |
| 1311 _printExpression(token); | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1315 void _printExpression(Token token) { | |
| 1316 var current = token; | |
| 1317 var end = new ClassMemberParser(this).skipExpression(current); | |
| 1318 var str = new StringBuffer(); | |
| 1319 while (current != end) { | |
| 1320 if (!["(", ",", ")"].contains(current.value)) str.write(' '); | |
| 1321 str.write(current.value); | |
| 1322 current = current.next; | |
| 1323 } | |
| 1324 print('exp: $str'); | |
| 1325 } | |
| 1326 } | |
| 1327 | |
| 1328 /// Builder for constant expressions. | |
| 1329 /// | |
| 1330 /// Any invalid subexpression is denoted with [Invalid]. | |
| 1331 class ConstExpressionBuilder extends ExpressionListener { | |
| 1332 bool get forConst => true; | |
| 1333 final Uri uri; | |
| 1334 Parser parser; | |
| 1335 ConstExpressionBuilder(this.uri) { | |
| 1336 parser = new Parser(this, | |
| 1337 asyncAwaitKeywordsEnabled: true); | |
| 1338 } | |
| 1339 | |
| 1340 void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | |
| 1341 debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | |
| 1342 } | |
| 1343 | |
| 1344 void handleAsOperator(Token op, Token next) { | |
| 1345 debugEvent("As"); | |
| 1346 if (ignore) return; | |
| 1347 push(new As(pop(), pop())); | |
| 1348 } | |
| 1349 | |
| 1350 void handleIndexedExpression(Token openSquareBracket, Token token) { | |
| 1351 debugEvent("Index"); | |
| 1352 if (ignore) return; | |
| 1353 pop(); // receiver | |
| 1354 pop(); // index | |
| 1355 push(new Invalid(hint: "index")); | |
| 1356 } | |
| 1357 | |
| 1358 void handleAssignmentExpression(Token operator) { | |
| 1359 pop(); // lhs | |
| 1360 pop(); // rhs | |
| 1361 push(new Invalid(hint: "assign")); | |
| 1362 } | |
| 1363 | |
| 1364 void handleUnaryPrefixAssignmentExpression(Token operator) { | |
| 1365 pop(); | |
| 1366 push(new Invalid(hint: "prefixOp")); | |
| 1367 } | |
| 1368 | |
| 1369 void handleUnaryPostfixAssignmentExpression(Token operator) { | |
| 1370 pop(); | |
| 1371 push(new Invalid(hint: "postfixOp")); | |
| 1372 } | |
| 1373 | |
| 1374 void handleNamedArgument(colon) { | |
| 1375 debugEvent("NamedArg"); | |
| 1376 if (ignore) return; | |
| 1377 var value = pop(); | |
| 1378 Ref name = pop(); | |
| 1379 push(new NamedArg(name.name, value)); | |
| 1380 } | |
| 1381 | |
| 1382 void endArguments(int count, Token begin, Token end) { | |
| 1383 debugEvent("Arguments"); | |
| 1384 if (ignore) return; | |
| 1385 push(popList(count) ?? const []); | |
| 1386 } | |
| 1387 | |
| 1388 void handleNewExpression(Token token) { | |
| 1389 debugEvent("NewExpression"); | |
| 1390 if (ignore) return; | |
| 1391 pop(); // args | |
| 1392 pop(); // ctor | |
| 1393 push(new Invalid(hint: "new")); | |
| 1394 } | |
| 1395 | |
| 1396 void _endFunction() { | |
| 1397 assert(_withinFunction >= 0); | |
| 1398 push(new Invalid(hint: 'function')); | |
| 1399 } | |
| 1400 | |
| 1401 // TODO(paulberry): is this needed? | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:14
really depends on how we want to handle recovery o
 
Paul Berry
2017/03/09 23:26:23
We *do* expect to run the summary builder on inval
 
 | |
| 1402 //void _endCascade() { | |
| 1403 // push(new Invalid(hint: 'cascades')); | |
| 1404 //} | |
| 1405 | |
| 1406 void _unhandledSend() { | |
| 1407 push(new Invalid(hint: "call")); | |
| 1408 } | |
| 1409 } | |
| 1410 | |
| 1411 /// Builder for initializer expressions. These expressions exclude any nested | |
| 1412 /// expression that is not needed to infer strong mode types. | |
| 1413 class InitializerBuilder extends ExpressionListener { | |
| 1414 final Uri uri; | |
| 1415 Parser parser; | |
| 1416 | |
| 1417 InitializerBuilder(this.uri) { | |
| 1418 parser = new Parser(this, | |
| 1419 asyncAwaitKeywordsEnabled: true); | |
| 1420 } | |
| 1421 | |
| 1422 void handleNoConstructorReferenceContinuationAfterTypeArguments(Token token) { | |
| 1423 debugEvent("NoConstructorReferenceContinuationAfterTypeArguments"); | |
| 1424 } | |
| 1425 | |
| 1426 bool get ignore => super.ignore || _inArguments > 0; | |
| 1427 | |
| 1428 void handleAsOperator(Token op, Token next) { | |
| 1429 debugEvent("As"); | |
| 1430 if (ignore) return; | |
| 1431 TypeRef type = pop(); | |
| 1432 pop(); | |
| 1433 push(new Opaque(type: type)); | |
| 1434 } | |
| 1435 | |
| 1436 // Not necessary, but we don't use the value, so we can abstract it: | |
| 1437 void handleIsOperator(Token operator, Token not, Token endToken) { | |
| 1438 debugEvent("Is"); | |
| 1439 if (ignore) return; | |
| 1440 throw new UnimplementedError(); // TODO(paulberry): fix the code below. | |
| 
 
Siggi Cherem (dart-lang)
2017/03/09 21:02:13
is the fix related to references in general?
 
Paul Berry
2017/03/09 23:26:23
I don't know what the proper fix is.  The reason I
 
 | |
| 1441 // push(new Opaque(type: new TypeRef(new Ref('bool')))); | |
| 1442 } | |
| 1443 | |
| 1444 void handleIndexedExpression(Token openSquareBracket, Token token) { | |
| 1445 debugEvent("Index"); | |
| 1446 if (ignore) return; | |
| 1447 pop(); | |
| 1448 pop(); | |
| 1449 push(new Opaque()); | |
| 1450 } | |
| 1451 | |
| 1452 void handleAssignmentExpression(Token operator) { | |
| 1453 debugEvent("Assign"); | |
| 1454 if (ignore) return; | |
| 1455 var left = pop(); | |
| 1456 var right = pop(); | |
| 1457 var kind = operator.kind; | |
| 1458 if (kind == EQ_TOKEN) { | |
| 1459 push(new OpaqueOp(right)); | |
| 1460 } else { | |
| 1461 push(new OpaqueOp(new Binary(left, right, opForAssignOp(kind)))); | |
| 1462 } | |
| 1463 } | |
| 1464 | |
| 1465 void handleUnaryPrefixAssignmentExpression(Token operator) { | |
| 1466 debugEvent("Prefix"); | |
| 1467 if (ignore) return; | |
| 1468 var kind = operator.kind == PLUS_PLUS_TOKEN ? PLUS_TOKEN : MINUS_TOKEN; | |
| 1469 push(new OpaqueOp(new Binary(pop(), new IntLiteral(1), kind))); | |
| 1470 } | |
| 1471 | |
| 1472 void handleUnaryPostfixAssignmentExpression(Token operator) { | |
| 1473 debugEvent("PostFix"); | |
| 1474 if (ignore) return; | |
| 1475 // the post-fix effect is not visible to the enclosing expression | |
| 1476 push(new OpaqueOp(pop())); | |
| 1477 } | |
| 1478 | |
| 1479 int _inArguments = 0; | |
| 1480 void beginArguments(Token token) { | |
| 1481 // TODO(sigmund): determine if we can ignore arguments. | |
| 1482 //_inArguments++; | |
| 1483 } | |
| 1484 | |
| 1485 void handleNamedArgument(colon) { | |
| 1486 debugEvent("NamedArg"); | |
| 1487 if (ignore) return; | |
| 1488 pop(); | |
| 1489 pop(); | |
| 1490 push(NullValue.Arguments); | |
| 1491 } | |
| 1492 | |
| 1493 void endArguments(int count, Token begin, Token end) { | |
| 1494 debugEvent("Arguments"); | |
| 1495 //_inArguments--; | |
| 1496 if (ignore) return; | |
| 1497 push(popList(count) ?? const []); | |
| 1498 //push([new Opaque(hint: "arguments")]); | |
| 1499 } | |
| 1500 | |
| 1501 void handleNewExpression(Token token) { | |
| 1502 debugEvent("NewExpression"); | |
| 1503 if (ignore) return; | |
| 1504 pop(); // args | |
| 1505 /* var ctor = */ pop(); // ctor | |
| 1506 throw new UnimplementedError(); // TODO(paulberry): fix the code below. | |
| 1507 // push(new Opaque(type: ctor.type, hint: "new")); | |
| 1508 } | |
| 1509 | |
| 1510 void _endFunction() { | |
| 1511 push(new Opaque(hint: "function")); | |
| 1512 } | |
| 1513 | |
| 1514 // TODO(paulberry): is this needed? | |
| 1515 //void _endCascade() { | |
| 1516 // push(new OpaqueOp(pop(), hint: 'cascades')); | |
| 1517 //} | |
| 1518 | |
| 1519 void _unhandledSend() { | |
| 1520 push(new Opaque(hint: "call")); | |
| 1521 } | |
| 1522 } | |
| 1523 | |
| 1524 // bit-masks to encode modifiers as bits on an int. | |
| 1525 | |
| 1526 /// Maps modifier names to their bit-mask. | |
| 1527 const _modifierFlag = const { | |
| 1528 'const': _const_flag, | |
| 1529 'abstract': _abstract_flag, | |
| 1530 'static': _static_flag, | |
| 1531 'external': _external_flag, | |
| 1532 'final': _final_flag, | |
| 1533 'var': _var_flag, | |
| 1534 }; | |
| 1535 | |
| 1536 const _var_flag = 0; | |
| 1537 const _final_flag = 1; | |
| 1538 const _const_flag = 1 << 1; | |
| 1539 const _abstract_flag = 1 << 2; | |
| 1540 const _static_flag = 1 << 3; | |
| 1541 const _external_flag = 1 << 4; | |
| 1542 | |
| 1543 // bit-masks to encode async modifiers as bits on an int. | |
| 1544 | |
| 1545 const _async_flag = 1; | |
| 1546 const _sync_flag = 1 << 1; | |
| 1547 const _star_flag = 1 << 2; | |
| 1548 | |
| 1549 /// Retrieve the operator from an assignment operator (e.g. + from +=). | |
| 1550 /// Operators are encoded using the scanner token kind id. | |
| 1551 int opForAssignOp(int kind) { | |
| 1552 switch (kind) { | |
| 1553 case AMPERSAND_EQ_TOKEN: return AMPERSAND_TOKEN; | |
| 1554 // TODO(paulberry): add support for &&= | |
| 1555 // case AMPERSAND_AMPERSAND_EQ_TOKEN: return AMPERSAND_AMPERSAND_TOKEN; | |
| 1556 case BAR_EQ_TOKEN: return BAR_TOKEN; | |
| 1557 // TODO(paulberry): add support for ||= | |
| 1558 // case BAR_BAR_EQ_TOKEN: return BAR_BAR_TOKEN; | |
| 1559 case CARET_EQ_TOKEN: return CARET_TOKEN; | |
| 1560 case GT_GT_EQ_TOKEN: return GT_GT_TOKEN; | |
| 1561 case LT_LT_EQ_TOKEN: return LT_LT_TOKEN; | |
| 1562 case MINUS_EQ_TOKEN: return MINUS_TOKEN; | |
| 1563 case PERCENT_EQ_TOKEN: return PERCENT_TOKEN; | |
| 1564 case PLUS_EQ_TOKEN: return PLUS_TOKEN; | |
| 1565 case QUESTION_QUESTION_EQ_TOKEN: return QUESTION_QUESTION_TOKEN; | |
| 1566 case SLASH_EQ_TOKEN: return SLASH_TOKEN; | |
| 1567 case STAR_EQ_TOKEN: return STAR_TOKEN; | |
| 1568 case TILDE_SLASH_EQ_TOKEN: return TILDE_SLASH_TOKEN; | |
| 1569 case PLUS_EQ_TOKEN: return PLUS_TOKEN; | |
| 1570 default: | |
| 1571 throw "Unhandled kind $kind"; | |
| 1572 } | |
| 1573 } | |
| OLD | NEW |