Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of resolution; | 5 part of resolution; |
| 6 | 6 |
| 7 abstract class TreeElements { | 7 abstract class TreeElements { |
| 8 AnalyzableElement get analyzedElement; | 8 AnalyzableElement get analyzedElement; |
| 9 Iterable<Node> get superUses; | 9 Iterable<Node> get superUses; |
| 10 | 10 |
| 11 /// Iterables of the dependencies that this [TreeElement] records of | 11 /// Iterables of the dependencies that this [TreeElement] records of |
| 12 /// [analyzedElement]. | 12 /// [analyzedElement]. |
| 13 Iterable<Element> get allElements; | 13 Iterable<Element> get allElements; |
| 14 void forEachConstantNode(f(Node n, ConstExp c)); | 14 void forEachConstantNode(f(Node n, ConstantExpression c)); |
| 15 | 15 |
| 16 /// A set of additional dependencies. See [registerDependency] below. | 16 /// A set of additional dependencies. See [registerDependency] below. |
| 17 Iterable<Element> get otherDependencies; | 17 Iterable<Element> get otherDependencies; |
| 18 | 18 |
| 19 Element operator[](Node node); | 19 Element operator[](Node node); |
| 20 | 20 |
| 21 // TODO(johnniwinther): Investigate whether [Node] could be a [Send]. | 21 // TODO(johnniwinther): Investigate whether [Node] could be a [Send]. |
| 22 Selector getSelector(Node node); | 22 Selector getSelector(Node node); |
| 23 Selector getGetterSelectorInComplexSendSet(SendSet node); | 23 Selector getGetterSelectorInComplexSendSet(SendSet node); |
| 24 Selector getOperatorSelectorInComplexSendSet(SendSet node); | 24 Selector getOperatorSelectorInComplexSendSet(SendSet node); |
| 25 DartType getType(Node node); | 25 DartType getType(Node node); |
| 26 void setSelector(Node node, Selector selector); | 26 void setSelector(Node node, Selector selector); |
| 27 void setGetterSelectorInComplexSendSet(SendSet node, Selector selector); | 27 void setGetterSelectorInComplexSendSet(SendSet node, Selector selector); |
| 28 void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector); | 28 void setOperatorSelectorInComplexSendSet(SendSet node, Selector selector); |
| 29 | 29 |
| 30 /// Returns the for-in loop variable for [node]. | 30 /// Returns the for-in loop variable for [node]. |
| 31 Element getForInVariable(ForIn node); | 31 Element getForInVariable(ForIn node); |
| 32 Selector getIteratorSelector(ForIn node); | 32 Selector getIteratorSelector(ForIn node); |
| 33 Selector getMoveNextSelector(ForIn node); | 33 Selector getMoveNextSelector(ForIn node); |
| 34 Selector getCurrentSelector(ForIn node); | 34 Selector getCurrentSelector(ForIn node); |
| 35 void setIteratorSelector(ForIn node, Selector selector); | 35 void setIteratorSelector(ForIn node, Selector selector); |
| 36 void setMoveNextSelector(ForIn node, Selector selector); | 36 void setMoveNextSelector(ForIn node, Selector selector); |
| 37 void setCurrentSelector(ForIn node, Selector selector); | 37 void setCurrentSelector(ForIn node, Selector selector); |
| 38 void setConstant(Node node, ConstExp constant); | 38 void setConstant(Node node, ConstantExpression constant); |
| 39 ConstExp getConstant(Node node); | 39 ConstantExpression getConstant(Node node); |
| 40 bool isAssert(Send send); | 40 bool isAssert(Send send); |
| 41 | 41 |
| 42 /// Returns the [FunctionElement] defined by [node]. | 42 /// Returns the [FunctionElement] defined by [node]. |
| 43 FunctionElement getFunctionDefinition(FunctionExpression node); | 43 FunctionElement getFunctionDefinition(FunctionExpression node); |
| 44 | 44 |
| 45 /// Returns target constructor for the redirecting factory body [node]. | 45 /// Returns target constructor for the redirecting factory body [node]. |
| 46 ConstructorElement getRedirectingTargetConstructor( | 46 ConstructorElement getRedirectingTargetConstructor( |
| 47 RedirectingFactoryBody node); | 47 RedirectingFactoryBody node); |
| 48 | 48 |
| 49 /** | 49 /** |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 /// Returns the label that [node] targets. | 86 /// Returns the label that [node] targets. |
| 87 LabelDefinition getTargetLabel(GotoStatement node); | 87 LabelDefinition getTargetLabel(GotoStatement node); |
| 88 } | 88 } |
| 89 | 89 |
| 90 class TreeElementMapping implements TreeElements { | 90 class TreeElementMapping implements TreeElements { |
| 91 final AnalyzableElement analyzedElement; | 91 final AnalyzableElement analyzedElement; |
| 92 Map<Spannable, Selector> _selectors; | 92 Map<Spannable, Selector> _selectors; |
| 93 Map<Node, DartType> _types; | 93 Map<Node, DartType> _types; |
| 94 Setlet<Node> _superUses; | 94 Setlet<Node> _superUses; |
| 95 Setlet<Element> _otherDependencies; | 95 Setlet<Element> _otherDependencies; |
| 96 Map<Node, ConstExp> _constants; | 96 Map<Node, ConstantExpression> _constants; |
| 97 Map<VariableElement, List<Node>> _potentiallyMutated; | 97 Map<VariableElement, List<Node>> _potentiallyMutated; |
| 98 Map<Node, Map<VariableElement, List<Node>>> _potentiallyMutatedIn; | 98 Map<Node, Map<VariableElement, List<Node>>> _potentiallyMutatedIn; |
| 99 Map<VariableElement, List<Node>> _potentiallyMutatedInClosure; | 99 Map<VariableElement, List<Node>> _potentiallyMutatedInClosure; |
| 100 Map<Node, Map<VariableElement, List<Node>>> _accessedByClosureIn; | 100 Map<Node, Map<VariableElement, List<Node>>> _accessedByClosureIn; |
| 101 Setlet<Element> _elements; | 101 Setlet<Element> _elements; |
| 102 Setlet<Send> _asserts; | 102 Setlet<Send> _asserts; |
| 103 | 103 |
| 104 /// Map from nodes to the targets they define. | 104 /// Map from nodes to the targets they define. |
| 105 Map<Node, JumpTarget> _definedTargets; | 105 Map<Node, JumpTarget> _definedTargets; |
| 106 | 106 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 229 } | 229 } |
| 230 | 230 |
| 231 Selector getCurrentSelector(ForIn node) { | 231 Selector getCurrentSelector(ForIn node) { |
| 232 return _getSelector(node.inToken); | 232 return _getSelector(node.inToken); |
| 233 } | 233 } |
| 234 | 234 |
| 235 Element getForInVariable(ForIn node) { | 235 Element getForInVariable(ForIn node) { |
| 236 return this[node]; | 236 return this[node]; |
| 237 } | 237 } |
| 238 | 238 |
| 239 void setConstant(Node node, ConstExp constant) { | 239 void setConstant(Node node, ConstantExpression constant) { |
| 240 if (_constants == null) { | 240 if (_constants == null) { |
| 241 _constants = new Maplet<Node, ConstExp>(); | 241 _constants = new Maplet<Node, ConstantExpression>(); |
| 242 } | 242 } |
| 243 _constants[node] = constant; | 243 _constants[node] = constant; |
| 244 } | 244 } |
| 245 | 245 |
| 246 ConstExp getConstant(Node node) { | 246 ConstantExpression getConstant(Node node) { |
| 247 return _constants != null ? _constants[node] : null; | 247 return _constants != null ? _constants[node] : null; |
| 248 } | 248 } |
| 249 | 249 |
| 250 bool isTypeLiteral(Send node) { | 250 bool isTypeLiteral(Send node) { |
| 251 return getType(node) != null; | 251 return getType(node) != null; |
| 252 } | 252 } |
| 253 | 253 |
| 254 DartType getTypeLiteralType(Send node) { | 254 DartType getTypeLiteralType(Send node) { |
| 255 return getType(node); | 255 return getType(node); |
| 256 } | 256 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 337 () => new Maplet<VariableElement, List<Node>>()); | 337 () => new Maplet<VariableElement, List<Node>>()); |
| 338 accessMap.putIfAbsent(element, () => <Node>[]).add(accessNode); | 338 accessMap.putIfAbsent(element, () => <Node>[]).add(accessNode); |
| 339 } | 339 } |
| 340 | 340 |
| 341 String toString() => 'TreeElementMapping($analyzedElement)'; | 341 String toString() => 'TreeElementMapping($analyzedElement)'; |
| 342 | 342 |
| 343 Iterable<Element> get allElements { | 343 Iterable<Element> get allElements { |
| 344 return _elements != null ? _elements : const <Element>[]; | 344 return _elements != null ? _elements : const <Element>[]; |
| 345 } | 345 } |
| 346 | 346 |
| 347 void forEachConstantNode(f(Node n, ConstExp c)) { | 347 void forEachConstantNode(f(Node n, ConstantExpression c)) { |
| 348 if (_constants != null) { | 348 if (_constants != null) { |
| 349 _constants.forEach(f); | 349 _constants.forEach(f); |
| 350 } | 350 } |
| 351 } | 351 } |
| 352 | 352 |
| 353 void setAssert(Send node) { | 353 void setAssert(Send node) { |
| 354 if (_asserts == null) { | 354 if (_asserts == null) { |
| 355 _asserts = new Setlet<Send>(); | 355 _asserts = new Setlet<Send>(); |
| 356 } | 356 } |
| 357 _asserts.add(node); | 357 _asserts.add(node); |
| (...skipping 2944 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3302 registry.registerFactoryWithTypeArguments(); | 3302 registry.registerFactoryWithTypeArguments(); |
| 3303 } | 3303 } |
| 3304 if (constructor.isGenerativeConstructor && cls.isAbstract) { | 3304 if (constructor.isGenerativeConstructor && cls.isAbstract) { |
| 3305 warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION); | 3305 warning(node, MessageKind.ABSTRACT_CLASS_INSTANTIATION); |
| 3306 registry.registerAbstractClassInstantiation(); | 3306 registry.registerAbstractClassInstantiation(); |
| 3307 } | 3307 } |
| 3308 | 3308 |
| 3309 if (isSymbolConstructor) { | 3309 if (isSymbolConstructor) { |
| 3310 if (node.isConst) { | 3310 if (node.isConst) { |
| 3311 Node argumentNode = node.send.arguments.head; | 3311 Node argumentNode = node.send.arguments.head; |
| 3312 ConstExp constant = compiler.resolver.constantCompiler.compileNode( | 3312 ConstantExpression constant = compiler.resolver.constantCompiler.compile Node( |
|
sigurdm
2014/10/01 07:46:47
Long line
Johnni Winther
2014/10/01 08:21:23
Done.
| |
| 3313 argumentNode, registry.mapping); | 3313 argumentNode, registry.mapping); |
| 3314 Constant name = constant.value; | 3314 ConstantValue name = constant.value; |
| 3315 if (!name.isString) { | 3315 if (!name.isString) { |
| 3316 DartType type = name.computeType(compiler); | 3316 DartType type = name.computeType(compiler); |
| 3317 compiler.reportError(argumentNode, MessageKind.STRING_EXPECTED, | 3317 compiler.reportError(argumentNode, MessageKind.STRING_EXPECTED, |
| 3318 {'type': type}); | 3318 {'type': type}); |
| 3319 } else { | 3319 } else { |
| 3320 StringConstant stringConstant = name; | 3320 StringConstantValue stringConstant = name; |
| 3321 String nameString = stringConstant.toDartString().slowToString(); | 3321 String nameString = stringConstant.toDartString().slowToString(); |
| 3322 if (validateSymbol(argumentNode, nameString)) { | 3322 if (validateSymbol(argumentNode, nameString)) { |
| 3323 registry.registerConstSymbol(nameString); | 3323 registry.registerConstSymbol(nameString); |
| 3324 } | 3324 } |
| 3325 } | 3325 } |
| 3326 } else { | 3326 } else { |
| 3327 if (!compiler.mirrorUsageAnalyzerTask.hasMirrorUsage( | 3327 if (!compiler.mirrorUsageAnalyzerTask.hasMirrorUsage( |
| 3328 enclosingElement)) { | 3328 enclosingElement)) { |
| 3329 compiler.reportHint( | 3329 compiler.reportHint( |
| 3330 node.newToken, MessageKind.NON_CONST_BLOAT, | 3330 node.newToken, MessageKind.NON_CONST_BLOAT, |
| 3331 {'name': compiler.symbolClass.name}); | 3331 {'name': compiler.symbolClass.name}); |
| 3332 } | 3332 } |
| 3333 registry.registerNewSymbol(); | 3333 registry.registerNewSymbol(); |
| 3334 } | 3334 } |
| 3335 } else if (isMirrorsUsedConstant) { | 3335 } else if (isMirrorsUsedConstant) { |
| 3336 compiler.mirrorUsageAnalyzerTask.validate(node, registry.mapping); | 3336 compiler.mirrorUsageAnalyzerTask.validate(node, registry.mapping); |
| 3337 } | 3337 } |
| 3338 if (node.isConst) { | 3338 if (node.isConst) { |
| 3339 analyzeConstant(node); | 3339 analyzeConstant(node); |
| 3340 } | 3340 } |
| 3341 | 3341 |
| 3342 return null; | 3342 return null; |
| 3343 } | 3343 } |
| 3344 | 3344 |
| 3345 void checkConstMapKeysDontOverrideEquals(Spannable spannable, | 3345 void checkConstMapKeysDontOverrideEquals(Spannable spannable, |
| 3346 MapConstant map) { | 3346 MapConstantValue map) { |
| 3347 for (Constant key in map.keys) { | 3347 for (ConstantValue key in map.keys) { |
| 3348 if (!key.isObject) continue; | 3348 if (!key.isObject) continue; |
| 3349 ObjectConstant objectConstant = key; | 3349 ObjectConstantValue objectConstant = key; |
| 3350 DartType keyType = objectConstant.type; | 3350 DartType keyType = objectConstant.type; |
| 3351 ClassElement cls = keyType.element; | 3351 ClassElement cls = keyType.element; |
| 3352 if (cls == compiler.stringClass) continue; | 3352 if (cls == compiler.stringClass) continue; |
| 3353 Element equals = cls.lookupMember('=='); | 3353 Element equals = cls.lookupMember('=='); |
| 3354 if (equals.enclosingClass != compiler.objectClass) { | 3354 if (equals.enclosingClass != compiler.objectClass) { |
| 3355 compiler.reportError(spannable, | 3355 compiler.reportError(spannable, |
| 3356 MessageKind.CONST_MAP_KEY_OVERRIDES_EQUALS, | 3356 MessageKind.CONST_MAP_KEY_OVERRIDES_EQUALS, |
| 3357 {'type': keyType}); | 3357 {'type': keyType}); |
| 3358 } | 3358 } |
| 3359 } | 3359 } |
| 3360 } | 3360 } |
| 3361 | 3361 |
| 3362 void analyzeConstant(Node node) { | 3362 void analyzeConstant(Node node) { |
| 3363 addDeferredAction(enclosingElement, () { | 3363 addDeferredAction(enclosingElement, () { |
| 3364 ConstExp constant = compiler.resolver.constantCompiler.compileNode( | 3364 ConstantExpression constant = compiler.resolver.constantCompiler.compileNo de( |
|
sigurdm
2014/10/01 07:46:47
Long line
Johnni Winther
2014/10/01 08:21:23
Done.
| |
| 3365 node, registry.mapping); | 3365 node, registry.mapping); |
| 3366 | 3366 |
| 3367 Constant value = constant.value; | 3367 ConstantValue value = constant.value; |
| 3368 if (value.isMap) { | 3368 if (value.isMap) { |
| 3369 checkConstMapKeysDontOverrideEquals(node, value); | 3369 checkConstMapKeysDontOverrideEquals(node, value); |
| 3370 } | 3370 } |
| 3371 | 3371 |
| 3372 // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names | 3372 // The type constant that is an argument to JS_INTERCEPTOR_CONSTANT names |
| 3373 // a class that will be instantiated outside the program by attaching a | 3373 // a class that will be instantiated outside the program by attaching a |
| 3374 // native class dispatch record referencing the interceptor. | 3374 // native class dispatch record referencing the interceptor. |
| 3375 if (argumentsToJsInterceptorConstant != null && | 3375 if (argumentsToJsInterceptorConstant != null && |
| 3376 argumentsToJsInterceptorConstant.contains(node)) { | 3376 argumentsToJsInterceptorConstant.contains(node)) { |
| 3377 if (value.isType) { | 3377 if (value.isType) { |
| 3378 TypeConstant typeConstant = value; | 3378 TypeConstantValue typeConstant = value; |
| 3379 if (typeConstant.representedType is InterfaceType) { | 3379 if (typeConstant.representedType is InterfaceType) { |
| 3380 registry.registerInstantiatedType(typeConstant.representedType); | 3380 registry.registerInstantiatedType(typeConstant.representedType); |
| 3381 } else { | 3381 } else { |
| 3382 compiler.reportError(node, | 3382 compiler.reportError(node, |
| 3383 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); | 3383 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); |
| 3384 } | 3384 } |
| 3385 } else { | 3385 } else { |
| 3386 compiler.reportError(node, | 3386 compiler.reportError(node, |
| 3387 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); | 3387 MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT); |
| 3388 } | 3388 } |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3704 } | 3704 } |
| 3705 | 3705 |
| 3706 visitLiteralMapEntry(LiteralMapEntry node) { | 3706 visitLiteralMapEntry(LiteralMapEntry node) { |
| 3707 node.visitChildren(this); | 3707 node.visitChildren(this); |
| 3708 } | 3708 } |
| 3709 | 3709 |
| 3710 visitNamedArgument(NamedArgument node) { | 3710 visitNamedArgument(NamedArgument node) { |
| 3711 visit(node.expression); | 3711 visit(node.expression); |
| 3712 } | 3712 } |
| 3713 | 3713 |
| 3714 DartType typeOfConstant(Constant constant) { | 3714 DartType typeOfConstant(ConstantValue constant) { |
| 3715 if (constant.isInt) return compiler.intClass.rawType; | 3715 if (constant.isInt) return compiler.intClass.rawType; |
| 3716 if (constant.isBool) return compiler.boolClass.rawType; | 3716 if (constant.isBool) return compiler.boolClass.rawType; |
| 3717 if (constant.isDouble) return compiler.doubleClass.rawType; | 3717 if (constant.isDouble) return compiler.doubleClass.rawType; |
| 3718 if (constant.isString) return compiler.stringClass.rawType; | 3718 if (constant.isString) return compiler.stringClass.rawType; |
| 3719 if (constant.isNull) return compiler.nullClass.rawType; | 3719 if (constant.isNull) return compiler.nullClass.rawType; |
| 3720 if (constant.isFunction) return compiler.functionClass.rawType; | 3720 if (constant.isFunction) return compiler.functionClass.rawType; |
| 3721 assert(constant.isObject); | 3721 assert(constant.isObject); |
| 3722 ObjectConstant objectConstant = constant; | 3722 ObjectConstantValue objectConstant = constant; |
| 3723 return objectConstant.type; | 3723 return objectConstant.type; |
| 3724 } | 3724 } |
| 3725 | 3725 |
| 3726 bool overridesEquals(DartType type) { | 3726 bool overridesEquals(DartType type) { |
| 3727 ClassElement cls = type.element; | 3727 ClassElement cls = type.element; |
| 3728 Element equals = cls.lookupMember('=='); | 3728 Element equals = cls.lookupMember('=='); |
| 3729 return equals.enclosingClass != compiler.objectClass; | 3729 return equals.enclosingClass != compiler.objectClass; |
| 3730 } | 3730 } |
| 3731 | 3731 |
| 3732 void checkCaseExpressions(SwitchStatement node) { | 3732 void checkCaseExpressions(SwitchStatement node) { |
| 3733 JumpTarget breakElement = getOrDefineTarget(node); | 3733 JumpTarget breakElement = getOrDefineTarget(node); |
| 3734 Map<String, LabelDefinition> continueLabels = <String, LabelDefinition>{}; | 3734 Map<String, LabelDefinition> continueLabels = <String, LabelDefinition>{}; |
| 3735 | 3735 |
| 3736 Link<Node> cases = node.cases.nodes; | 3736 Link<Node> cases = node.cases.nodes; |
| 3737 CaseMatch firstCase = null; | 3737 CaseMatch firstCase = null; |
| 3738 DartType firstCaseType = null; | 3738 DartType firstCaseType = null; |
| 3739 bool hasReportedProblem = false; | 3739 bool hasReportedProblem = false; |
| 3740 | 3740 |
| 3741 for (Link<Node> cases = node.cases.nodes; | 3741 for (Link<Node> cases = node.cases.nodes; |
| 3742 !cases.isEmpty; | 3742 !cases.isEmpty; |
| 3743 cases = cases.tail) { | 3743 cases = cases.tail) { |
| 3744 SwitchCase switchCase = cases.head; | 3744 SwitchCase switchCase = cases.head; |
| 3745 | 3745 |
| 3746 for (Node labelOrCase in switchCase.labelsAndCases) { | 3746 for (Node labelOrCase in switchCase.labelsAndCases) { |
| 3747 CaseMatch caseMatch = labelOrCase.asCaseMatch(); | 3747 CaseMatch caseMatch = labelOrCase.asCaseMatch(); |
| 3748 if (caseMatch == null) continue; | 3748 if (caseMatch == null) continue; |
| 3749 | 3749 |
| 3750 // Analyze the constant. | 3750 // Analyze the constant. |
| 3751 ConstExp constant = registry.getConstant(caseMatch.expression); | 3751 ConstantExpression constant = registry.getConstant(caseMatch.expression) ; |
|
sigurdm
2014/10/01 07:46:47
long line
Johnni Winther
2014/10/01 08:21:23
Done.
| |
| 3752 assert(invariant(node, constant != null, | 3752 assert(invariant(node, constant != null, |
| 3753 message: 'No constant computed for $node')); | 3753 message: 'No constant computed for $node')); |
| 3754 | 3754 |
| 3755 DartType caseType = typeOfConstant(constant.value); | 3755 DartType caseType = typeOfConstant(constant.value); |
| 3756 | 3756 |
| 3757 if (firstCaseType == null) { | 3757 if (firstCaseType == null) { |
| 3758 firstCase = caseMatch; | 3758 firstCase = caseMatch; |
| 3759 firstCaseType = caseType; | 3759 firstCaseType = caseType; |
| 3760 | 3760 |
| 3761 // We only report the bad type on the first class element. All others | 3761 // We only report the bad type on the first class element. All others |
| (...skipping 1200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4962 } | 4962 } |
| 4963 | 4963 |
| 4964 /// The result for the resolution of the `assert` method. | 4964 /// The result for the resolution of the `assert` method. |
| 4965 class AssertResult implements ResolutionResult { | 4965 class AssertResult implements ResolutionResult { |
| 4966 const AssertResult(); | 4966 const AssertResult(); |
| 4967 | 4967 |
| 4968 Element get element => null; | 4968 Element get element => null; |
| 4969 | 4969 |
| 4970 String toString() => 'AssertResult()'; | 4970 String toString() => 'AssertResult()'; |
| 4971 } | 4971 } |
| OLD | NEW |