| 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 #library('elements'); | 5 #library('elements'); |
| 6 | 6 |
| 7 #import('dart:uri'); | 7 #import('dart:uri'); |
| 8 | 8 |
| 9 #import('../tree/tree.dart'); | 9 #import('../tree/tree.dart'); |
| 10 #import('../scanner/scannerlib.dart'); | 10 #import('../scanner/scannerlib.dart'); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 | 101 |
| 102 toString() => id; | 102 toString() => id; |
| 103 } | 103 } |
| 104 | 104 |
| 105 class Element implements Hashable { | 105 class Element implements Hashable { |
| 106 final SourceString name; | 106 final SourceString name; |
| 107 final ElementKind kind; | 107 final ElementKind kind; |
| 108 final Element enclosingElement; | 108 final Element enclosingElement; |
| 109 Link<Node> metadata = const EmptyLink<Node>(); | 109 Link<Node> metadata = const EmptyLink<Node>(); |
| 110 | 110 |
| 111 | |
| 112 Element(this.name, this.kind, this.enclosingElement) { | 111 Element(this.name, this.kind, this.enclosingElement) { |
| 113 assert(getLibrary() !== null); | 112 assert(getLibrary() !== null); |
| 114 } | 113 } |
| 115 | 114 |
| 116 Modifiers get modifiers() => null; | 115 Modifiers get modifiers() => null; |
| 117 | 116 |
| 118 Node parseNode(DiagnosticListener listener) { | 117 Node parseNode(DiagnosticListener listener) { |
| 119 listener.cancel("Internal Error: $this.parseNode", token: position()); | 118 listener.cancel("Internal Error: $this.parseNode", token: position()); |
| 120 } | 119 } |
| 121 | 120 |
| 122 Type computeType(Compiler compiler) { | 121 Type computeType(Compiler compiler) { |
| 123 compiler.internalError("$this.computeType.", token: position()); | 122 compiler.internalError("$this.computeType.", token: position()); |
| 124 } | 123 } |
| 125 | 124 |
| 126 void addMetadata(Node node) { | 125 void addMetadata(Node node) { |
| 127 metadata = metadata.prepend(node); | 126 metadata = metadata.prepend(node); |
| 128 } | 127 } |
| 129 | 128 |
| 130 bool isFunction() => kind === ElementKind.FUNCTION; | 129 bool isFunction() => kind === ElementKind.FUNCTION; |
| 131 bool isConstructor() => isFactoryConstructor() || isGenerativeConstructor(); | 130 bool isConstructor() => isFactoryConstructor() || isGenerativeConstructor(); |
| 132 bool isClosure() => false; | 131 bool isClosure() => false; |
| 133 bool isMember() { | 132 bool isMember() { |
| 134 // Check that this element is defined in the scope of a Class. | 133 // Check that this element is defined in the scope of a Class. |
| 135 Element enclosing = enclosingElement; | 134 Element enclosing = enclosingElement; |
| 136 if (enclosing !== null && | 135 if (enclosing !== null && |
| 137 enclosing.kind === ElementKind.COMPILATION_UNIT_OVERRIDE) { | 136 enclosing.kind === ElementKind.COMPILATION_UNIT_OVERRIDE) { |
| 138 enclosing = enclosing.enclosingElement; | 137 enclosing = enclosing.enclosingElement; |
| 139 } | 138 } |
| 140 // TODO(lrn): Skip any synthetic elements inserted, e.g., | |
| 141 // a compilation unit override. | |
| 142 return enclosing !== null && enclosing.isClass(); | 139 return enclosing !== null && enclosing.isClass(); |
| 143 } | 140 } |
| 144 bool isInstanceMember() => false; | 141 bool isInstanceMember() => false; |
| 145 bool isFactoryConstructor() => modifiers !== null && modifiers.isFactory(); | 142 bool isFactoryConstructor() => modifiers !== null && modifiers.isFactory(); |
| 146 bool isGenerativeConstructor() => kind === ElementKind.GENERATIVE_CONSTRUCTOR; | 143 bool isGenerativeConstructor() => kind === ElementKind.GENERATIVE_CONSTRUCTOR; |
| 147 bool isGenerativeConstructorBody() => | 144 bool isGenerativeConstructorBody() => |
| 148 kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY; | 145 kind === ElementKind.GENERATIVE_CONSTRUCTOR_BODY; |
| 149 bool isCompilationUnit() { | 146 bool isCompilationUnit() => kind === ElementKind.COMPILATION_UNIT; |
| 150 return kind === ElementKind.COMPILATION_UNIT || | |
| 151 kind === ElementKind.LIBRARY || | |
| 152 kind === ElementKind.COMPILATION_UNIT_OVERRIDE; | |
| 153 } | |
| 154 /** | |
| 155 * Provides the compilation unit corresponding to this element. | |
| 156 * Returns non-null for elements where [isCompilationUnit] returns true, | |
| 157 * but may not return the current [Element]. | |
| 158 */ | |
| 159 CompilationUnitElement asCompilationUnit() => null; | |
| 160 bool isClass() => kind === ElementKind.CLASS; | 147 bool isClass() => kind === ElementKind.CLASS; |
| 161 bool isPrefix() => kind === ElementKind.PREFIX; | 148 bool isPrefix() => kind === ElementKind.PREFIX; |
| 162 bool isVariable() => kind === ElementKind.VARIABLE; | 149 bool isVariable() => kind === ElementKind.VARIABLE; |
| 163 bool isParameter() => kind === ElementKind.PARAMETER; | 150 bool isParameter() => kind === ElementKind.PARAMETER; |
| 164 bool isStatement() => kind === ElementKind.STATEMENT; | 151 bool isStatement() => kind === ElementKind.STATEMENT; |
| 165 bool isTypedef() => kind === ElementKind.TYPEDEF; | 152 bool isTypedef() => kind === ElementKind.TYPEDEF; |
| 166 bool isTypeVariable() => kind === ElementKind.TYPE_VARIABLE; | 153 bool isTypeVariable() => kind === ElementKind.TYPE_VARIABLE; |
| 167 bool isField() => kind === ElementKind.FIELD; | 154 bool isField() => kind === ElementKind.FIELD; |
| 168 bool isGetter() => kind === ElementKind.GETTER; | 155 bool isGetter() => kind === ElementKind.GETTER; |
| 169 bool isSetter() => kind === ElementKind.SETTER; | 156 bool isSetter() => kind === ElementKind.SETTER; |
| 170 bool isAccessor() => isGetter() || isSetter(); | 157 bool isAccessor() => isGetter() || isSetter(); |
| 171 bool isForeign() => kind === ElementKind.FOREIGN; | 158 bool isForeign() => kind === ElementKind.FOREIGN; |
| 172 bool isLibrary() => kind === ElementKind.LIBRARY; | 159 bool isLibrary() => kind === ElementKind.LIBRARY; |
| 173 bool impliesType() => (kind.category & ElementCategory.IMPLIES_TYPE) != 0; | 160 bool impliesType() => (kind.category & ElementCategory.IMPLIES_TYPE) != 0; |
| 174 bool isExtendable() => (kind.category & ElementCategory.IS_EXTENDABLE) != 0; | 161 bool isExtendable() => (kind.category & ElementCategory.IS_EXTENDABLE) != 0; |
| 175 | 162 |
| 176 // TODO(johnniwinther): This breaks for libraries (for which enclosing | 163 // TODO(johnniwinther): This breaks for libraries (for which enclosing |
| 177 // elements are null) and is invalid for top level variable declarations for | 164 // elements are null) and is invalid for top level variable declarations for |
| 178 // which the enclosing element is a VariableDeclarations and not a compilation | 165 // which the enclosing element is a VariableDeclarations and not a compilation |
| 179 // unit. | 166 // unit. |
| 180 bool isTopLevel() => enclosingElement.isCompilationUnit(); | 167 bool isTopLevel() { |
| 168 return enclosingElement !== null && enclosingElement.isCompilationUnit(); |
| 169 } |
| 181 | 170 |
| 182 bool isAssignable() { | 171 bool isAssignable() { |
| 183 if (modifiers != null && modifiers.isFinal()) return false; | 172 if (modifiers != null && modifiers.isFinal()) return false; |
| 184 if (isFunction() || isGenerativeConstructor()) return false; | 173 if (isFunction() || isGenerativeConstructor()) return false; |
| 185 return true; | 174 return true; |
| 186 } | 175 } |
| 187 | 176 |
| 188 Token position() => null; | 177 Token position() => null; |
| 189 | 178 |
| 190 Token findMyName(Token token) { | 179 Token findMyName(Token token) { |
| 191 for (Token t = token; t.kind !== EOF_TOKEN; t = t.next) { | 180 for (Token t = token; t.kind !== EOF_TOKEN; t = t.next) { |
| 192 if (t.value == name) return t; | 181 if (t.value == name) return t; |
| 193 } | 182 } |
| 194 return token; | 183 return token; |
| 195 } | 184 } |
| 196 | 185 |
| 197 // TODO(kasperl): This is a very bad hash code for the element and | 186 // TODO(kasperl): This is a very bad hash code for the element and |
| 198 // there's no reason why two elements with the same name should have | 187 // there's no reason why two elements with the same name should have |
| 199 // the same hash code. Replace this with a simple id in the element? | 188 // the same hash code. Replace this with a simple id in the element? |
| 200 int hashCode() => name === null ? 0 : name.hashCode(); | 189 int hashCode() => name === null ? 0 : name.hashCode(); |
| 201 | 190 |
| 202 Script getScript() { | |
| 203 return getCompilationUnit().script; | |
| 204 } | |
| 205 | |
| 206 CompilationUnitElement getCompilationUnit() { | 191 CompilationUnitElement getCompilationUnit() { |
| 207 if (isCompilationUnit()) return this; | 192 Element element = this; |
| 208 return enclosingElement.getCompilationUnit(); | 193 while (element !== null && !element.isCompilationUnit()) { |
| 194 if (element is CompilationUnitOverrideElement) { |
| 195 CompilationUnitOverrideElement override = element; |
| 196 return override.compilationUnit; |
| 197 } |
| 198 if (element.isLibrary()) { |
| 199 LibraryElement library = element; |
| 200 return library.entryCompilationUnit; |
| 201 } |
| 202 element = element.enclosingElement; |
| 203 if (element is FunctionElement) { |
| 204 FunctionElement function = element; |
| 205 if (function.isPatched) { |
| 206 element = function.patch; |
| 207 } |
| 208 } |
| 209 } |
| 210 return element; |
| 209 } | 211 } |
| 210 | 212 |
| 211 LibraryElement getLibrary() { | 213 LibraryElement getLibrary() { |
| 212 Element element = this; | 214 Element element = this; |
| 213 while (element.kind !== ElementKind.LIBRARY) { | 215 while (element.kind !== ElementKind.LIBRARY) { |
| 214 element = element.enclosingElement; | 216 element = element.enclosingElement; |
| 215 } | 217 } |
| 216 return element; | 218 return element; |
| 217 } | 219 } |
| 218 | 220 |
| 219 ClassElement getEnclosingClass() { | 221 ClassElement getEnclosingClass() { |
| 220 for (Element e = this; e !== null; e = e.enclosingElement) { | 222 for (Element e = this; e !== null; e = e.enclosingElement) { |
| 221 if (e.isClass()) return e; | 223 if (e.isClass()) return e; |
| 222 } | 224 } |
| 223 return null; | 225 return null; |
| 224 } | 226 } |
| 225 | 227 |
| 228 Element getEnclosingClassOrCompilationUnit() { |
| 229 for (Element e = this; e !== null; e = e.enclosingElement) { |
| 230 if (e.isClass() || e.isCompilationUnit()) return e; |
| 231 } |
| 232 return null; |
| 233 } |
| 234 |
| 226 Element getEnclosingMember() { | 235 Element getEnclosingMember() { |
| 227 for (Element e = this; e !== null; e = e.enclosingElement) { | 236 for (Element e = this; e !== null; e = e.enclosingElement) { |
| 228 if (e.isMember()) return e; | 237 if (e.isMember()) return e; |
| 229 } | 238 } |
| 230 return null; | 239 return null; |
| 231 } | 240 } |
| 232 | 241 |
| 233 Element getOutermostEnclosingMemberOrTopLevel() { | 242 Element getOutermostEnclosingMemberOrTopLevel() { |
| 234 // TODO(lrn): Why is this called "Outermost"? | 243 // TODO(lrn): Why is this called "Outermost"? |
| 235 for (Element e = this; e !== null; e = e.enclosingElement) { | 244 for (Element e = this; e !== null; e = e.enclosingElement) { |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 listener.cancel('duplicate definition', token: element.position()); | 322 listener.cancel('duplicate definition', token: element.position()); |
| 314 listener.cancel('existing definition', token: existing.position()); | 323 listener.cancel('existing definition', token: existing.position()); |
| 315 } | 324 } |
| 316 } | 325 } |
| 317 } | 326 } |
| 318 | 327 |
| 319 Element localLookup(SourceString elementName) { | 328 Element localLookup(SourceString elementName) { |
| 320 return localScope[elementName]; | 329 return localScope[elementName]; |
| 321 } | 330 } |
| 322 | 331 |
| 323 void addGetterOrSetter(Element element, | 332 void addGetterOrSetter(FunctionElement element, |
| 324 Element existing, | 333 Element existing, |
| 325 DiagnosticListener listener) { | 334 DiagnosticListener listener) { |
| 326 void reportError(Element other) { | 335 void reportError(Element other) { |
| 327 // TODO(ahe): Do something similar to Resolver.reportErrorWithContext. | 336 // TODO(ahe): Do something similar to Resolver.reportErrorWithContext. |
| 328 listener.cancel('duplicate definition of ${element.name.slowToString()}', | 337 listener.cancel('duplicate definition of ${element.name.slowToString()}', |
| 329 element: element); | 338 element: element); |
| 330 listener.cancel('existing definition', element: other); | 339 listener.cancel('existing definition', element: other); |
| 331 } | 340 } |
| 332 | 341 |
| 333 if (existing != null) { | 342 if (existing != null) { |
| 334 if (existing.kind !== ElementKind.ABSTRACT_FIELD) { | 343 if (existing.kind !== ElementKind.ABSTRACT_FIELD) { |
| 335 reportError(existing); | 344 reportError(existing); |
| 336 } else { | 345 } else { |
| 337 AbstractFieldElement field = existing; | 346 AbstractFieldElement field = existing; |
| 338 if (element.kind == ElementKind.GETTER) { | 347 if (element.kind == ElementKind.GETTER) { |
| 339 if (field.getter != null && field.getter != element) { | 348 if (field.getter != null && field.getter != element) { |
| 340 reportError(field.getter); | 349 reportError(field.getter); |
| 341 } | 350 } |
| 342 field.getter = element; | 351 field.getter = element; |
| 343 } else { | 352 } else { |
| 344 if (field.setter != null && field.setter != element) { | 353 if (field.setter != null && field.setter != element) { |
| 345 reportError(field.setter); | 354 reportError(field.setter); |
| 346 } | 355 } |
| 347 field.setter = element; | 356 field.setter = element; |
| 348 } | 357 } |
| 349 } | 358 } |
| 350 } else { | 359 } else { |
| 351 AbstractFieldElement field = new AbstractFieldElement(element.name, this); | 360 Element container = element.getEnclosingClassOrCompilationUnit(); |
| 361 AbstractFieldElement field = |
| 362 new AbstractFieldElement(element.name, container); |
| 352 if (element.kind == ElementKind.GETTER) { | 363 if (element.kind == ElementKind.GETTER) { |
| 353 field.getter = element; | 364 field.getter = element; |
| 354 } else { | 365 } else { |
| 355 field.setter = element; | 366 field.setter = element; |
| 356 } | 367 } |
| 357 addMember(field, listener); | 368 addMember(field, listener); |
| 358 } | 369 } |
| 359 } | 370 } |
| 360 } | 371 } |
| 361 | 372 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 378 | 389 |
| 379 class CompilationUnitOverrideElement extends Element { | 390 class CompilationUnitOverrideElement extends Element { |
| 380 final CompilationUnitElement compilationUnit; | 391 final CompilationUnitElement compilationUnit; |
| 381 | 392 |
| 382 CompilationUnitOverrideElement(CompilationUnitElement compilationUnit, | 393 CompilationUnitOverrideElement(CompilationUnitElement compilationUnit, |
| 383 Element enclosing) | 394 Element enclosing) |
| 384 : this.compilationUnit = compilationUnit, | 395 : this.compilationUnit = compilationUnit, |
| 385 super(compilationUnit.name, | 396 super(compilationUnit.name, |
| 386 ElementKind.COMPILATION_UNIT_OVERRIDE, | 397 ElementKind.COMPILATION_UNIT_OVERRIDE, |
| 387 enclosing); | 398 enclosing); |
| 388 | |
| 389 CompilationUnitElement asCompilationUnit() => compilationUnit; | |
| 390 | |
| 391 CompilationUnitElement getCompilationUnit() => compilationUnit; | |
| 392 } | 399 } |
| 393 | 400 |
| 394 class LibraryElement extends ScopeContainerElement { | 401 class LibraryElement extends ScopeContainerElement { |
| 395 CompilationUnitElement entryCompilationUnit; | 402 CompilationUnitElement entryCompilationUnit; |
| 396 Link<CompilationUnitElement> compilationUnits = | 403 Link<CompilationUnitElement> compilationUnits = |
| 397 const EmptyLink<CompilationUnitElement>(); | 404 const EmptyLink<CompilationUnitElement>(); |
| 398 | 405 |
| 399 Link<ScriptTag> tags = const EmptyLink<ScriptTag>(); | 406 Link<ScriptTag> tags = const EmptyLink<ScriptTag>(); |
| 400 ScriptTag libraryTag; | 407 ScriptTag libraryTag; |
| 401 bool canUseNative = false; | 408 bool canUseNative = false; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 454 String getLibraryOrScriptName() { | 461 String getLibraryOrScriptName() { |
| 455 if (libraryTag !== null) { | 462 if (libraryTag !== null) { |
| 456 return libraryTag.argument.dartString.slowToString(); | 463 return libraryTag.argument.dartString.slowToString(); |
| 457 } else { | 464 } else { |
| 458 // Use the file name as script name. | 465 // Use the file name as script name. |
| 459 String path = uri.path; | 466 String path = uri.path; |
| 460 return path.substring(path.lastIndexOf('/') + 1); | 467 return path.substring(path.lastIndexOf('/') + 1); |
| 461 } | 468 } |
| 462 } | 469 } |
| 463 | 470 |
| 464 CompilationUnitElement getCompilationUnit() => entryCompilationUnit; | |
| 465 | |
| 466 Scope buildEnclosingScope() => new TopScope(this); | 471 Scope buildEnclosingScope() => new TopScope(this); |
| 467 } | 472 } |
| 468 | 473 |
| 469 class PrefixElement extends Element { | 474 class PrefixElement extends Element { |
| 470 Map<SourceString, Element> imported; | 475 Map<SourceString, Element> imported; |
| 471 Token firstPosition; | 476 Token firstPosition; |
| 472 | 477 |
| 473 PrefixElement(SourceString prefix, Element enclosing, this.firstPosition) | 478 PrefixElement(SourceString prefix, Element enclosing, this.firstPosition) |
| 474 : imported = new Map<SourceString, Element>(), | 479 : imported = new Map<SourceString, Element>(), |
| 475 super(prefix, ElementKind.PREFIX, enclosing); | 480 super(prefix, ElementKind.PREFIX, enclosing); |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 692 | 697 |
| 693 Node parseNode(DiagnosticListener listener) { | 698 Node parseNode(DiagnosticListener listener) { |
| 694 throw "internal error: AbstractFieldElement has no node"; | 699 throw "internal error: AbstractFieldElement has no node"; |
| 695 } | 700 } |
| 696 | 701 |
| 697 position() { | 702 position() { |
| 698 // The getter and setter may be defined in two different | 703 // The getter and setter may be defined in two different |
| 699 // compilation units. However, we know that one of them is | 704 // compilation units. However, we know that one of them is |
| 700 // non-null and defined in the same compilation unit as the | 705 // non-null and defined in the same compilation unit as the |
| 701 // abstract element. | 706 // abstract element. |
| 707 // TODO(lrn): No we don't know that if the element from the same |
| 708 // compilation unit is patched. |
| 702 // | 709 // |
| 703 // We need to make sure that the position returned is relative to | 710 // We need to make sure that the position returned is relative to |
| 704 // the compilation unit of the abstract element. | 711 // the compilation unit of the abstract element. |
| 705 if (getter !== null | 712 if (getter !== null |
| 706 && getter.getCompilationUnit() === getCompilationUnit()) { | 713 && getter.getCompilationUnit() === getCompilationUnit()) { |
| 707 return getter.position(); | 714 return getter.position(); |
| 708 } else { | 715 } else { |
| 709 return setter.position(); | 716 return setter.position(); |
| 710 } | 717 } |
| 711 } | 718 } |
| 712 | 719 |
| 713 Modifiers get modifiers() { | 720 Modifiers get modifiers() { |
| 714 // The resolver ensures that the flags match (ignoring abstract). | 721 // The resolver ensures that the flags match (ignoring abstract). |
| 715 if (getter !== null) { | 722 if (getter !== null) { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 804 FunctionElement.tooMuchOverloading(SourceString name, | 811 FunctionElement.tooMuchOverloading(SourceString name, |
| 805 FunctionExpression this.cachedNode, | 812 FunctionExpression this.cachedNode, |
| 806 ElementKind kind, | 813 ElementKind kind, |
| 807 Modifiers this.modifiers, | 814 Modifiers this.modifiers, |
| 808 Element enclosing, | 815 Element enclosing, |
| 809 FunctionSignature this.functionSignature) | 816 FunctionSignature this.functionSignature) |
| 810 : super(name, kind, enclosing) { | 817 : super(name, kind, enclosing) { |
| 811 defaultImplementation = this; | 818 defaultImplementation = this; |
| 812 } | 819 } |
| 813 | 820 |
| 814 Script getScript() { | |
| 815 if (patch !== null) return patch.getScript(); | |
| 816 return super.getScript(); | |
| 817 } | |
| 818 | |
| 819 bool get isPatched() => patch !== null; | 821 bool get isPatched() => patch !== null; |
| 820 | 822 |
| 821 /** | 823 /** |
| 822 * Applies a patch function to this function. The patch function's body | 824 * Applies a patch function to this function. The patch function's body |
| 823 * is used as replacement when parsing this function's body. | 825 * is used as replacement when parsing this function's body. |
| 824 * This method must not be called after the function has been parsed, | 826 * This method must not be called after the function has been parsed, |
| 825 * and it must be called at most once. | 827 * and it must be called at most once. |
| 826 */ | 828 */ |
| 827 void setPatch(FunctionElement patchElement) { | 829 void setPatch(FunctionElement patchElement) { |
| 828 // Sanity checks. The caller must check these things before calling. | 830 // Sanity checks. The caller must check these things before calling. |
| (...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1449 Node parseNode(compiler) => cachedNode; | 1451 Node parseNode(compiler) => cachedNode; |
| 1450 | 1452 |
| 1451 String toString() => "${enclosingElement.toString()}.${name.slowToString()}"; | 1453 String toString() => "${enclosingElement.toString()}.${name.slowToString()}"; |
| 1452 | 1454 |
| 1453 TypeVariableElement cloneTo(Element enclosing, DiagnosticListener listener) { | 1455 TypeVariableElement cloneTo(Element enclosing, DiagnosticListener listener) { |
| 1454 TypeVariableElement result = | 1456 TypeVariableElement result = |
| 1455 new TypeVariableElement(name, enclosing, node, type, bound); | 1457 new TypeVariableElement(name, enclosing, node, type, bound); |
| 1456 return result; | 1458 return result; |
| 1457 } | 1459 } |
| 1458 } | 1460 } |
| OLD | NEW |