| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, 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 part of dart_backend; | |
| 6 | |
| 7 // TODO(ahe): This class is simply wrong. This backend should use | |
| 8 // elements when it can, not AST nodes. Perhaps a [Map<Element, | |
| 9 // TreeElements>] is what is needed. | |
| 10 class ElementAst { | |
| 11 final Node ast; | |
| 12 final TreeElements treeElements; | |
| 13 | |
| 14 ElementAst(this.ast, this.treeElements); | |
| 15 } | |
| 16 | |
| 17 class DartBackend extends Backend { | |
| 18 final List<CompilerTask> tasks; | |
| 19 final bool stripAsserts; | |
| 20 | |
| 21 bool get supportsReflection => true; | |
| 22 | |
| 23 // TODO(zarah) Maybe change this to a command-line option. | |
| 24 // Right now, it is set by the tests. | |
| 25 bool useMirrorHelperLibrary = false; | |
| 26 | |
| 27 /// Updated to a [MirrorRenamerImpl] instance if the [useMirrorHelperLibrary] | |
| 28 /// field is set and mirror are needed. | |
| 29 MirrorRenamer mirrorRenamer = const MirrorRenamer(); | |
| 30 | |
| 31 final DartOutputter outputter; | |
| 32 | |
| 33 // Used in test. | |
| 34 PlaceholderRenamer get placeholderRenamer => outputter.renamer; | |
| 35 Map<ClassNode, List<Node>> get memberNodes => outputter.output.memberNodes; | |
| 36 | |
| 37 ConstantSystem get constantSystem { | |
| 38 return constantCompilerTask.constantCompiler.constantSystem; | |
| 39 } | |
| 40 | |
| 41 BackendConstantEnvironment get constants => constantCompilerTask; | |
| 42 | |
| 43 DartConstantTask constantCompilerTask; | |
| 44 | |
| 45 DartImpactTransformer impactTransformer; | |
| 46 | |
| 47 final Set<ClassElement> usedTypeLiterals = new Set<ClassElement>(); | |
| 48 | |
| 49 /// The set of visible platform classes that are implemented by instantiated | |
| 50 /// user classes. | |
| 51 final Set<ClassElement> _userImplementedPlatformClasses = | |
| 52 new Set<ClassElement>(); | |
| 53 | |
| 54 bool enableCodegenWithErrorsIfSupported(Spannable node) { | |
| 55 reporter.reportHintMessage(node, MessageKind.GENERIC, { | |
| 56 'text': "Generation of code with compile time errors is not " | |
| 57 "supported for dart2dart." | |
| 58 }); | |
| 59 return false; | |
| 60 } | |
| 61 | |
| 62 /** | |
| 63 * Tells whether it is safe to remove type declarations from variables, | |
| 64 * functions parameters. It becomes not safe if: | |
| 65 * 1) TypeError is used somewhere in the code, | |
| 66 * 2) The code has typedefs in right hand side of IS checks, | |
| 67 * 3) The code has classes which extend typedefs, have type arguments typedefs | |
| 68 * or type variable bounds typedefs. | |
| 69 * These restrictions can be less strict. | |
| 70 */ | |
| 71 bool isSafeToRemoveTypeDeclarations( | |
| 72 Map<ClassElement, Iterable<Element>> classMembers) { | |
| 73 ClassElement typeErrorElement = compiler.coreLibrary.find('TypeError'); | |
| 74 if (classMembers.containsKey(typeErrorElement) || | |
| 75 compiler.resolverWorld.isChecks | |
| 76 .any((DartType type) => type.element == typeErrorElement)) { | |
| 77 return false; | |
| 78 } | |
| 79 Set<DartType> processedTypes = new Set<DartType>(); | |
| 80 List<DartType> workQueue = new List<DartType>(); | |
| 81 workQueue | |
| 82 .addAll(classMembers.keys.map((classElement) => classElement.thisType)); | |
| 83 workQueue.addAll(compiler.resolverWorld.isChecks); | |
| 84 | |
| 85 while (!workQueue.isEmpty) { | |
| 86 DartType type = workQueue.removeLast(); | |
| 87 if (processedTypes.contains(type)) continue; | |
| 88 processedTypes.add(type); | |
| 89 if (type is FunctionType) return false; | |
| 90 if (type is TypedefType) return false; | |
| 91 if (type is InterfaceType) { | |
| 92 InterfaceType interfaceType = type; | |
| 93 // Check all type arguments. | |
| 94 interfaceType.typeArguments.forEach(workQueue.add); | |
| 95 ClassElement element = type.element; | |
| 96 // Check all supertypes. | |
| 97 if (element.allSupertypes != null) { | |
| 98 element.allSupertypes.forEach(workQueue.add); | |
| 99 } | |
| 100 } | |
| 101 } | |
| 102 return true; | |
| 103 } | |
| 104 | |
| 105 DartBackend(Compiler compiler, List<String> strips, {bool multiFile}) | |
| 106 : tasks = <CompilerTask>[], | |
| 107 stripAsserts = strips.indexOf('asserts') != -1, | |
| 108 constantCompilerTask = new DartConstantTask(compiler), | |
| 109 outputter = new DartOutputter( | |
| 110 compiler.reporter, compiler.outputProvider, | |
| 111 forceStripTypes: strips.indexOf('types') != -1, | |
| 112 multiFile: multiFile, | |
| 113 enableMinification: compiler.options.enableMinification), | |
| 114 super(compiler) { | |
| 115 impactTransformer = new DartImpactTransformer(this); | |
| 116 } | |
| 117 | |
| 118 DiagnosticReporter get reporter => compiler.reporter; | |
| 119 | |
| 120 Resolution get resolution => compiler.resolution; | |
| 121 | |
| 122 bool classNeedsRti(ClassElement cls) => false; | |
| 123 bool methodNeedsRti(FunctionElement function) => false; | |
| 124 | |
| 125 void enqueueHelpers(ResolutionEnqueuer world, Registry registry) { | |
| 126 // Right now resolver doesn't always resolve interfaces needed | |
| 127 // for literals, so force them. TODO(antonm): fix in the resolver. | |
| 128 final LITERAL_TYPE_NAMES = const [ | |
| 129 'Map', | |
| 130 'List', | |
| 131 'num', | |
| 132 'int', | |
| 133 'double', | |
| 134 'bool' | |
| 135 ]; | |
| 136 final coreLibrary = compiler.coreLibrary; | |
| 137 for (final name in LITERAL_TYPE_NAMES) { | |
| 138 ClassElement classElement = coreLibrary.findLocal(name); | |
| 139 classElement.ensureResolved(resolution); | |
| 140 } | |
| 141 // Enqueue the methods that the VM might invoke on user objects because | |
| 142 // we don't trust the resolution to always get these included. | |
| 143 world.registerDynamicUse(new DynamicUse(Selectors.toString_, null)); | |
| 144 world.registerDynamicUse(new DynamicUse(Selectors.hashCode_, null)); | |
| 145 world.registerDynamicUse( | |
| 146 new DynamicUse(new Selector.binaryOperator('=='), null)); | |
| 147 world.registerDynamicUse(new DynamicUse(Selectors.compareTo, null)); | |
| 148 } | |
| 149 | |
| 150 WorldImpact codegen(CodegenWorkItem work) { | |
| 151 return const WorldImpact(); | |
| 152 } | |
| 153 | |
| 154 /** | |
| 155 * Tells whether we should output given element. Corelib classes like | |
| 156 * Object should not be in the resulting code. | |
| 157 */ | |
| 158 @override | |
| 159 bool shouldOutput(Element element) { | |
| 160 return (!element.library.isPlatformLibrary && | |
| 161 !element.isSynthesized && | |
| 162 element is! AbstractFieldElement) || | |
| 163 mirrorRenamer.isMirrorHelperLibrary(element.library); | |
| 164 } | |
| 165 | |
| 166 int assembleProgram() { | |
| 167 ElementAst computeElementAst(AstElement element) { | |
| 168 return new ElementAst( | |
| 169 element.resolvedAst.node, element.resolvedAst.elements); | |
| 170 } | |
| 171 | |
| 172 // TODO(johnniwinther): Remove the need for this method. | |
| 173 void postProcessElementAst(AstElement element, ElementAst elementAst, | |
| 174 newTypedefElementCallback, newClassElementCallback) { | |
| 175 ReferencedElementCollector collector = new ReferencedElementCollector( | |
| 176 reporter, | |
| 177 element, | |
| 178 elementAst, | |
| 179 newTypedefElementCallback, | |
| 180 newClassElementCallback); | |
| 181 collector.collect(); | |
| 182 } | |
| 183 | |
| 184 int totalSize = outputter.assembleProgram( | |
| 185 libraries: compiler.libraryLoader.libraries, | |
| 186 instantiatedClasses: compiler.resolverWorld.directlyInstantiatedClasses, | |
| 187 resolvedElements: compiler.enqueuer.resolution.processedElements, | |
| 188 usedTypeLiterals: usedTypeLiterals, | |
| 189 postProcessElementAst: postProcessElementAst, | |
| 190 computeElementAst: computeElementAst, | |
| 191 shouldOutput: shouldOutput, | |
| 192 isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations, | |
| 193 sortElements: Elements.sortedByPosition, | |
| 194 mirrorRenamer: mirrorRenamer, | |
| 195 mainFunction: compiler.mainFunction, | |
| 196 outputUri: compiler.options.outputUri); | |
| 197 | |
| 198 // Output verbose info about size ratio of resulting bundle to all | |
| 199 // referenced non-platform sources. | |
| 200 logResultBundleSizeInfo(outputter.libraryInfo.userLibraries, | |
| 201 outputter.elementInfo.topLevelElements, totalSize); | |
| 202 | |
| 203 return totalSize; | |
| 204 } | |
| 205 | |
| 206 void logResultBundleSizeInfo(Iterable<LibraryElement> userLibraries, | |
| 207 Iterable<Element> topLevelElements, int totalOutputSize) { | |
| 208 // Sum total size of scripts in each referenced library. | |
| 209 int nonPlatformSize = 0; | |
| 210 for (LibraryElement lib in userLibraries) { | |
| 211 for (CompilationUnitElement compilationUnit in lib.compilationUnits) { | |
| 212 nonPlatformSize += compilationUnit.script.file.length; | |
| 213 } | |
| 214 } | |
| 215 int percentage = totalOutputSize * 100 ~/ nonPlatformSize; | |
| 216 log('Total used non-platform files size: ${nonPlatformSize} bytes, ' | |
| 217 'Output total size: $totalOutputSize bytes (${percentage}%)'); | |
| 218 } | |
| 219 | |
| 220 log(String message) => reporter.log('[DartBackend] $message'); | |
| 221 | |
| 222 @override | |
| 223 Future onLibrariesLoaded(LoadedLibraries loadedLibraries) { | |
| 224 // All platform classes must be resolved to ensure that their member names | |
| 225 // are preserved. | |
| 226 loadedLibraries.forEachLibrary((LibraryElement library) { | |
| 227 if (library.isPlatformLibrary) { | |
| 228 library.forEachLocalMember((Element element) { | |
| 229 if (element.isClass) { | |
| 230 ClassElement classElement = element; | |
| 231 classElement.ensureResolved(resolution); | |
| 232 } | |
| 233 }); | |
| 234 } | |
| 235 }); | |
| 236 if (useMirrorHelperLibrary && | |
| 237 loadedLibraries.containsLibrary(Uris.dart_mirrors)) { | |
| 238 return compiler.libraryLoader | |
| 239 .loadLibrary(compiler.resolvedUriTranslator.translate( | |
| 240 loadedLibraries.getLibrary(Uris.dart_mirrors), | |
| 241 MirrorRenamerImpl.DART_MIRROR_HELPER, | |
| 242 null)) | |
| 243 .then((LibraryElement library) { | |
| 244 mirrorRenamer = new MirrorRenamerImpl(compiler, this, library); | |
| 245 }); | |
| 246 } | |
| 247 return new Future.value(); | |
| 248 } | |
| 249 | |
| 250 @override | |
| 251 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
| 252 if (element == compiler.mirrorSystemGetNameFunction) { | |
| 253 FunctionElement getNameFunction = mirrorRenamer.getNameFunction; | |
| 254 if (getNameFunction != null) { | |
| 255 enqueuer.addToWorkList(getNameFunction); | |
| 256 } | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 @override | |
| 261 void registerInstantiatedType( | |
| 262 InterfaceType type, Enqueuer enqueuer, Registry registry, | |
| 263 {bool mirrorUsage: false}) { | |
| 264 registerPlatformMembers(type, registerUse: registry.registerDynamicUse); | |
| 265 super.registerInstantiatedType(type, enqueuer, registry, | |
| 266 mirrorUsage: mirrorUsage); | |
| 267 } | |
| 268 | |
| 269 /// Register dynamic access of members of [type] that implement members | |
| 270 /// of types defined in the platform libraries. | |
| 271 void registerPlatformMembers(InterfaceType type, | |
| 272 {void registerUse(DynamicUse dynamicUse)}) { | |
| 273 // Without patching, dart2dart has no way of performing sound tree-shaking | |
| 274 // in face external functions. Therefore we employ another scheme: | |
| 275 // | |
| 276 // Based on the assumption that the platform code only relies on the | |
| 277 // interfaces of it's own classes, we can approximate the semantics of | |
| 278 // external functions by eagerly registering dynamic invocation of instance | |
| 279 // members defined the platform interfaces. | |
| 280 // | |
| 281 // Since we only need to generate code for non-platform classes we can | |
| 282 // restrict this registration to platform interfaces implemented by | |
| 283 // instantiated non-platform classes. | |
| 284 // | |
| 285 // Consider for instance this program: | |
| 286 // | |
| 287 // import 'dart:math' show Random; | |
| 288 // | |
| 289 // class MyRandom implements Random { | |
| 290 // int nextInt() => 0; | |
| 291 // } | |
| 292 // | |
| 293 // main() { | |
| 294 // print([0, 1, 2].shuffle(new MyRandom())); | |
| 295 // } | |
| 296 // | |
| 297 // Here `MyRandom` is a subtype if `Random` defined in 'dart:math'. By the | |
| 298 // assumption, all methods defined `Random` are potentially called, and | |
| 299 // therefore, though there are no visible call sites from the user node, | |
| 300 // dynamic invocation of for instance `nextInt` should be registered. In | |
| 301 // this case, `nextInt` is actually called by the standard implementation of | |
| 302 // `shuffle`. | |
| 303 | |
| 304 ClassElement cls = type.element; | |
| 305 if (!cls.library.isPlatformLibrary) { | |
| 306 for (Link<DartType> link = cls.allSupertypes; | |
| 307 !link.isEmpty; | |
| 308 link = link.tail) { | |
| 309 InterfaceType supertype = link.head; | |
| 310 ClassElement superclass = supertype.element; | |
| 311 LibraryElement library = superclass.library; | |
| 312 if (library.isPlatformLibrary) { | |
| 313 if (_userImplementedPlatformClasses.add(superclass)) { | |
| 314 // Register selectors for all instance methods since these might | |
| 315 // be called on user classes from within the platform | |
| 316 // implementation. | |
| 317 superclass.forEachLocalMember((MemberElement element) { | |
| 318 if (element.isConstructor || element.isStatic) return; | |
| 319 | |
| 320 element.computeType(resolution); | |
| 321 Selector selector = new Selector.fromElement(element); | |
| 322 registerUse(new DynamicUse(selector, null)); | |
| 323 }); | |
| 324 } | |
| 325 } | |
| 326 } | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 @override | |
| 331 bool enableDeferredLoadingIfSupported(Spannable node, Registry registry) { | |
| 332 // TODO(sigurdm): Implement deferred loading for dart2dart. | |
| 333 reporter.reportWarningMessage( | |
| 334 node, MessageKind.DEFERRED_LIBRARY_DART_2_DART); | |
| 335 return false; | |
| 336 } | |
| 337 | |
| 338 @override | |
| 339 Uri resolvePatchUri(String libraryName, Uri) { | |
| 340 // Dart2dart does not use patches. | |
| 341 return null; | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 class DartImpactTransformer extends ImpactTransformer { | |
| 346 final DartBackend backend; | |
| 347 | |
| 348 DartImpactTransformer(this.backend); | |
| 349 | |
| 350 @override | |
| 351 WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) { | |
| 352 TransformedWorldImpact transformed = | |
| 353 new TransformedWorldImpact(worldImpact); | |
| 354 for (TypeUse typeUse in worldImpact.typeUses) { | |
| 355 if (typeUse.kind == TypeUseKind.TYPE_LITERAL && | |
| 356 typeUse.type.isInterfaceType) { | |
| 357 backend.usedTypeLiterals.add(typeUse.type.element); | |
| 358 } | |
| 359 if (typeUse.kind == TypeUseKind.INSTANTIATION) { | |
| 360 backend.registerPlatformMembers(typeUse.type, | |
| 361 registerUse: transformed.registerDynamicUse); | |
| 362 } | |
| 363 } | |
| 364 return transformed; | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 class EmitterUnparser extends Unparser { | |
| 369 final Map<Node, String> renames; | |
| 370 | |
| 371 EmitterUnparser(this.renames, {bool minify, bool stripTypes}) | |
| 372 : super(minify: minify, stripTypes: stripTypes); | |
| 373 | |
| 374 visit(Node node) { | |
| 375 if (node != null && renames.containsKey(node)) { | |
| 376 write(renames[node]); | |
| 377 } else { | |
| 378 super.visit(node); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 unparseSendReceiver(Send node, {bool spacesNeeded: false}) { | |
| 383 // TODO(smok): Remove ugly hack for library prefices. | |
| 384 if (node.receiver != null && renames[node.receiver] == '') return; | |
| 385 super.unparseSendReceiver(node, spacesNeeded: spacesNeeded); | |
| 386 } | |
| 387 | |
| 388 unparseFunctionName(Node name) { | |
| 389 if (name != null && renames.containsKey(name)) { | |
| 390 write(renames[name]); | |
| 391 } else { | |
| 392 super.unparseFunctionName(name); | |
| 393 } | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 /** | |
| 398 * Some elements are not recorded by resolver now, | |
| 399 * for example, typedefs or classes which are only | |
| 400 * used in signatures, as/is operators or in super clauses | |
| 401 * (just to name a few). Retraverse AST to pick those up. | |
| 402 */ | |
| 403 class ReferencedElementCollector extends Visitor { | |
| 404 final DiagnosticReporter reporter; | |
| 405 final Element element; | |
| 406 final ElementAst elementAst; | |
| 407 final newTypedefElementCallback; | |
| 408 final newClassElementCallback; | |
| 409 | |
| 410 ReferencedElementCollector(this.reporter, this.element, this.elementAst, | |
| 411 this.newTypedefElementCallback, this.newClassElementCallback); | |
| 412 | |
| 413 visitNode(Node node) { | |
| 414 node.visitChildren(this); | |
| 415 } | |
| 416 | |
| 417 visitTypeAnnotation(TypeAnnotation typeAnnotation) { | |
| 418 TreeElements treeElements = elementAst.treeElements; | |
| 419 final DartType type = treeElements.getType(typeAnnotation); | |
| 420 assert(invariant(typeAnnotation, type != null, | |
| 421 message: "Missing type for type annotation: $treeElements.")); | |
| 422 if (type.isTypedef) newTypedefElementCallback(type.element); | |
| 423 if (type.isInterfaceType) newClassElementCallback(type.element); | |
| 424 typeAnnotation.visitChildren(this); | |
| 425 } | |
| 426 | |
| 427 void collect() { | |
| 428 reporter.withCurrentElement(element, () { | |
| 429 elementAst.ast.accept(this); | |
| 430 }); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 Comparator compareBy(f) => (x, y) => f(x).compareTo(f(y)); | |
| 435 | |
| 436 List sorted(Iterable l, comparison) { | |
| 437 final result = new List.from(l); | |
| 438 result.sort(comparison); | |
| 439 return result; | |
| 440 } | |
| 441 | |
| 442 compareElements(e0, e1) { | |
| 443 int result = compareBy((e) => e.library.canonicalUri.toString())(e0, e1); | |
| 444 if (result != 0) return result; | |
| 445 return compareBy((e) => e.position.charOffset)(e0, e1); | |
| 446 } | |
| 447 | |
| 448 /// [ConstantCompilerTask] for compilation of constants for the Dart backend. | |
| 449 /// | |
| 450 /// Since this task needs no distinction between frontend and backend constants | |
| 451 /// it also serves as the [BackendConstantEnvironment]. | |
| 452 class DartConstantTask extends ConstantCompilerTask | |
| 453 implements BackendConstantEnvironment { | |
| 454 final DartConstantCompiler constantCompiler; | |
| 455 | |
| 456 DartConstantTask(Compiler compiler) | |
| 457 : this.constantCompiler = new DartConstantCompiler(compiler), | |
| 458 super(compiler.measurer); | |
| 459 | |
| 460 String get name => 'ConstantHandler'; | |
| 461 | |
| 462 @override | |
| 463 ConstantSystem get constantSystem => constantCompiler.constantSystem; | |
| 464 | |
| 465 @override | |
| 466 bool hasConstantValue(ConstantExpression expression) { | |
| 467 return constantCompiler.hasConstantValue(expression); | |
| 468 } | |
| 469 | |
| 470 @override | |
| 471 ConstantValue getConstantValue(ConstantExpression expression) { | |
| 472 return constantCompiler.getConstantValue(expression); | |
| 473 } | |
| 474 | |
| 475 @override | |
| 476 ConstantValue getConstantValueForVariable(VariableElement element) { | |
| 477 return constantCompiler.getConstantValueForVariable(element); | |
| 478 } | |
| 479 | |
| 480 @override | |
| 481 ConstantExpression getConstantForNode(Node node, TreeElements elements) { | |
| 482 return constantCompiler.getConstantForNode(node, elements); | |
| 483 } | |
| 484 | |
| 485 @override | |
| 486 ConstantValue getConstantValueForNode(Node node, TreeElements elements) { | |
| 487 return getConstantValue( | |
| 488 constantCompiler.getConstantForNode(node, elements)); | |
| 489 } | |
| 490 | |
| 491 @override | |
| 492 ConstantValue getConstantValueForMetadata(MetadataAnnotation metadata) { | |
| 493 return getConstantValue(metadata.constant); | |
| 494 } | |
| 495 | |
| 496 @override | |
| 497 ConstantExpression compileConstant(VariableElement element) { | |
| 498 return measure(() { | |
| 499 return constantCompiler.compileConstant(element); | |
| 500 }); | |
| 501 } | |
| 502 | |
| 503 @override | |
| 504 void evaluate(ConstantExpression constant) { | |
| 505 return measure(() { | |
| 506 return constantCompiler.evaluate(constant); | |
| 507 }); | |
| 508 } | |
| 509 | |
| 510 @override | |
| 511 ConstantExpression compileVariable(VariableElement element) { | |
| 512 return measure(() { | |
| 513 return constantCompiler.compileVariable(element); | |
| 514 }); | |
| 515 } | |
| 516 | |
| 517 @override | |
| 518 ConstantExpression compileNode(Node node, TreeElements elements, | |
| 519 {bool enforceConst: true}) { | |
| 520 return measure(() { | |
| 521 return constantCompiler.compileNodeWithDefinitions(node, elements, | |
| 522 isConst: enforceConst); | |
| 523 }); | |
| 524 } | |
| 525 | |
| 526 @override | |
| 527 ConstantExpression compileMetadata( | |
| 528 MetadataAnnotation metadata, Node node, TreeElements elements) { | |
| 529 return measure(() { | |
| 530 return constantCompiler.compileMetadata(metadata, node, elements); | |
| 531 }); | |
| 532 } | |
| 533 | |
| 534 // TODO(johnniwinther): Remove this when values are computed from the | |
| 535 // expressions. | |
| 536 @override | |
| 537 void copyConstantValues(DartConstantTask task) { | |
| 538 constantCompiler.constantValueMap | |
| 539 .addAll(task.constantCompiler.constantValueMap); | |
| 540 } | |
| 541 | |
| 542 @override | |
| 543 void registerLazyStatic(FieldElement element) { | |
| 544 // Do nothing. | |
| 545 } | |
| 546 } | |
| OLD | NEW |