| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library kernel.analyzer.ast_from_analyzer; | 4 library kernel.analyzer.ast_from_analyzer; |
| 5 | 5 |
| 6 import '../ast.dart' as ast; | 6 import '../ast.dart' as ast; |
| 7 import '../frontend/accessors.dart'; | 7 import '../frontend/accessors.dart'; |
| 8 import '../frontend/super_initializers.dart'; | 8 import '../frontend/super_initializers.dart'; |
| 9 import '../log.dart'; | 9 import '../log.dart'; |
| 10 import '../type_algebra.dart'; | 10 import '../type_algebra.dart'; |
| (...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 521 } | 521 } |
| 522 | 522 |
| 523 ast.Statement buildMandatoryFunctionBody(FunctionBody body) { | 523 ast.Statement buildMandatoryFunctionBody(FunctionBody body) { |
| 524 try { | 524 try { |
| 525 if (body is BlockFunctionBody) { | 525 if (body is BlockFunctionBody) { |
| 526 return buildStatement(body.block); | 526 return buildStatement(body.block); |
| 527 } else if (body is ExpressionFunctionBody) { | 527 } else if (body is ExpressionFunctionBody) { |
| 528 if (bodyHasVoidReturn(body)) { | 528 if (bodyHasVoidReturn(body)) { |
| 529 return new ast.ExpressionStatement(buildExpression(body.expression)); | 529 return new ast.ExpressionStatement(buildExpression(body.expression)); |
| 530 } else { | 530 } else { |
| 531 return new ast.ReturnStatement(buildExpression(body.expression)); | 531 return new ast.ReturnStatement(buildExpression(body.expression)) |
| 532 ..fileOffset = body.expression.offset; |
| 532 } | 533 } |
| 533 } else { | 534 } else { |
| 534 return internalError('Missing function body'); | 535 return internalError('Missing function body'); |
| 535 } | 536 } |
| 536 } on _CompilationError catch (e) { | 537 } on _CompilationError catch (e) { |
| 537 return new ast.ExpressionStatement(buildThrowCompileTimeError(e.message)); | 538 return new ast.ExpressionStatement(buildThrowCompileTimeError(e.message)); |
| 538 } | 539 } |
| 539 } | 540 } |
| 540 | 541 |
| 541 ast.AsyncMarker getAsyncMarker({bool isAsync: false, bool isStar: false}) { | 542 ast.AsyncMarker getAsyncMarker({bool isAsync: false, bool isStar: false}) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 571 positional.add(declaration); | 572 positional.add(declaration); |
| 572 break; | 573 break; |
| 573 | 574 |
| 574 case ParameterKind.NAMED: | 575 case ParameterKind.NAMED: |
| 575 named.add(declaration); | 576 named.add(declaration); |
| 576 break; | 577 break; |
| 577 } | 578 } |
| 578 } | 579 } |
| 579 int offset = formalParameters?.offset ?? body.offset; | 580 int offset = formalParameters?.offset ?? body.offset; |
| 580 int endOffset = body.endToken.offset; | 581 int endOffset = body.endToken.offset; |
| 581 ast.AsyncMarker asyncMarker = getAsyncMarker( | 582 ast.AsyncMarker asyncMarker = |
| 582 isAsync: body.isAsynchronous, isStar: body.isGenerator); | 583 getAsyncMarker(isAsync: body.isAsynchronous, isStar: body.isGenerator); |
| 583 return new ast.FunctionNode(buildOptionalFunctionBody(body), | 584 return new ast.FunctionNode(buildOptionalFunctionBody(body), |
| 584 typeParameters: typeParameters, | 585 typeParameters: typeParameters, |
| 585 positionalParameters: positional, | 586 positionalParameters: positional, |
| 586 namedParameters: named, | 587 namedParameters: named, |
| 587 requiredParameterCount: requiredParameterCount, | 588 requiredParameterCount: requiredParameterCount, |
| 588 returnType: buildOptionalTypeAnnotation(returnType) ?? | 589 returnType: buildOptionalTypeAnnotation(returnType) ?? |
| 589 inferredReturnType ?? | 590 inferredReturnType ?? |
| 590 const ast.DynamicType(), | 591 const ast.DynamicType(), |
| 591 asyncMarker: asyncMarker, | 592 asyncMarker: asyncMarker, |
| 592 dartAsyncMarker: asyncMarker) | 593 dartAsyncMarker: asyncMarker) |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 663 return buildType(element.type); | 664 return buildType(element.type); |
| 664 } else if (element is VariableElement) { | 665 } else if (element is VariableElement) { |
| 665 return buildType(element.type); | 666 return buildType(element.type); |
| 666 } else { | 667 } else { |
| 667 log.severe('Unexpected variable element: $element'); | 668 log.severe('Unexpected variable element: $element'); |
| 668 return const ast.DynamicType(); | 669 return const ast.DynamicType(); |
| 669 } | 670 } |
| 670 } | 671 } |
| 671 | 672 |
| 672 ast.VariableDeclaration makeVariableDeclaration(LocalElement element, | 673 ast.VariableDeclaration makeVariableDeclaration(LocalElement element, |
| 673 {ast.DartType type, ast.Expression initializer}) { | 674 {ast.DartType type, ast.Expression initializer, int equalsOffset}) { |
| 674 var declaration = getVariableReference(element); | 675 var declaration = getVariableReference(element); |
| 676 if (equalsOffset != null) declaration.fileEqualsOffset = equalsOffset; |
| 675 declaration.type = type ?? getInferredVariableType(element); | 677 declaration.type = type ?? getInferredVariableType(element); |
| 676 if (initializer != null) { | 678 if (initializer != null) { |
| 677 declaration.initializer = initializer..parent = declaration; | 679 declaration.initializer = initializer..parent = declaration; |
| 678 } | 680 } |
| 679 return declaration; | 681 return declaration; |
| 680 } | 682 } |
| 681 | 683 |
| 682 /// Returns true if [arguments] can be accepted by [target] | 684 /// Returns true if [arguments] can be accepted by [target] |
| 683 /// (not taking type checks into account). | 685 /// (not taking type checks into account). |
| 684 bool areArgumentsCompatible( | 686 bool areArgumentsCompatible( |
| (...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 960 LabeledStatement labeled = node; | 962 LabeledStatement labeled = node; |
| 961 node = labeled.statement; | 963 node = labeled.statement; |
| 962 } | 964 } |
| 963 if (node is VariableDeclarationStatement) { | 965 if (node is VariableDeclarationStatement) { |
| 964 VariableDeclarationList list = node.variables; | 966 VariableDeclarationList list = node.variables; |
| 965 ast.DartType type = scope.buildOptionalTypeAnnotation(list.type); | 967 ast.DartType type = scope.buildOptionalTypeAnnotation(list.type); |
| 966 for (VariableDeclaration decl in list.variables) { | 968 for (VariableDeclaration decl in list.variables) { |
| 967 LocalElement local = decl.element as dynamic; // Cross cast. | 969 LocalElement local = decl.element as dynamic; // Cross cast. |
| 968 output.add(scope.makeVariableDeclaration(local, | 970 output.add(scope.makeVariableDeclaration(local, |
| 969 type: type, | 971 type: type, |
| 970 initializer: scope.buildOptionalExpression(decl.initializer))); | 972 initializer: scope.buildOptionalExpression(decl.initializer), |
| 973 equalsOffset: decl.equals?.offset)); |
| 971 } | 974 } |
| 972 } else { | 975 } else { |
| 973 output.add(build(node)); | 976 output.add(build(node)); |
| 974 } | 977 } |
| 975 } | 978 } |
| 976 | 979 |
| 977 ast.Statement makeBreakTarget(ast.Statement node, LabelStack stackNode) { | 980 ast.Statement makeBreakTarget(ast.Statement node, LabelStack stackNode) { |
| 978 if (stackNode.jumps.isEmpty) return node; | 981 if (stackNode.jumps.isEmpty) return node; |
| 979 var labeled = new ast.LabeledStatement(node); | 982 var labeled = new ast.LabeledStatement(node); |
| 980 for (var jump in stackNode.jumps) { | 983 for (var jump in stackNode.jumps) { |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1174 ast.Statement visitForStatement(ForStatement node) { | 1177 ast.Statement visitForStatement(ForStatement node) { |
| 1175 List<ast.VariableDeclaration> variables = <ast.VariableDeclaration>[]; | 1178 List<ast.VariableDeclaration> variables = <ast.VariableDeclaration>[]; |
| 1176 ast.Expression initialExpression; | 1179 ast.Expression initialExpression; |
| 1177 if (node.variables != null) { | 1180 if (node.variables != null) { |
| 1178 VariableDeclarationList list = node.variables; | 1181 VariableDeclarationList list = node.variables; |
| 1179 var type = scope.buildOptionalTypeAnnotation(list.type); | 1182 var type = scope.buildOptionalTypeAnnotation(list.type); |
| 1180 for (var variable in list.variables) { | 1183 for (var variable in list.variables) { |
| 1181 LocalElement local = variable.element as dynamic; // Cross cast. | 1184 LocalElement local = variable.element as dynamic; // Cross cast. |
| 1182 variables.add(scope.makeVariableDeclaration(local, | 1185 variables.add(scope.makeVariableDeclaration(local, |
| 1183 initializer: scope.buildOptionalExpression(variable.initializer), | 1186 initializer: scope.buildOptionalExpression(variable.initializer), |
| 1184 type: type)); | 1187 type: type, |
| 1188 equalsOffset: variable.equals?.offset)); |
| 1185 } | 1189 } |
| 1186 } else if (node.initialization != null) { | 1190 } else if (node.initialization != null) { |
| 1187 initialExpression = scope.buildExpression(node.initialization); | 1191 initialExpression = scope.buildExpression(node.initialization); |
| 1188 } | 1192 } |
| 1189 var breakNode = new LabelStack.unlabeled(breakStack); | 1193 var breakNode = new LabelStack.unlabeled(breakStack); |
| 1190 var continueNode = new LabelStack.unlabeled(continueStack); | 1194 var continueNode = new LabelStack.unlabeled(continueStack); |
| 1191 addLoopLabels(node, continueNode); | 1195 addLoopLabels(node, continueNode); |
| 1192 var body = buildInScope(node.body, breakNode, continueNode); | 1196 var body = buildInScope(node.body, breakNode, continueNode); |
| 1193 var loop = new ast.ForStatement( | 1197 var loop = new ast.ForStatement( |
| 1194 variables, | 1198 variables, |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1363 extends GeneralizingAstVisitor /* <ast.Expression | Accessor> */ { | 1367 extends GeneralizingAstVisitor /* <ast.Expression | Accessor> */ { |
| 1364 final ExpressionScope scope; | 1368 final ExpressionScope scope; |
| 1365 ast.VariableDeclaration cascadeReceiver; | 1369 ast.VariableDeclaration cascadeReceiver; |
| 1366 ExpressionBuilder(this.scope); | 1370 ExpressionBuilder(this.scope); |
| 1367 | 1371 |
| 1368 ast.Expression build(Expression node) { | 1372 ast.Expression build(Expression node) { |
| 1369 var result = node.accept(this); | 1373 var result = node.accept(this); |
| 1370 if (result is Accessor) { | 1374 if (result is Accessor) { |
| 1371 result = result.buildSimpleRead(); | 1375 result = result.buildSimpleRead(); |
| 1372 } | 1376 } |
| 1373 return result..fileOffset = _getOffset(node); | 1377 // For some method invocations we have already set a file offset to |
| 1378 // override the default behavior of _getOffset. |
| 1379 if (node is! MethodInvocation || result.fileOffset < 0) { |
| 1380 result.fileOffset = _getOffset(node); |
| 1381 } |
| 1382 return result; |
| 1374 } | 1383 } |
| 1375 | 1384 |
| 1376 int _getOffset(AstNode node) { | 1385 int _getOffset(AstNode node) { |
| 1377 if (node is MethodInvocation) { | 1386 if (node is MethodInvocation) { |
| 1378 return node.methodName.offset; | 1387 return node.methodName.offset; |
| 1379 } else if (node is InstanceCreationExpression) { | 1388 } else if (node is InstanceCreationExpression) { |
| 1380 return node.constructorName.offset; | 1389 return node.constructorName.offset; |
| 1381 } else if (node is BinaryExpression) { | 1390 } else if (node is BinaryExpression) { |
| 1382 return node.operator.offset; | 1391 return node.operator.offset; |
| 1383 } else if (node is PrefixedIdentifier) { | 1392 } else if (node is PrefixedIdentifier) { |
| 1384 return node.identifier.offset; | 1393 return node.identifier.offset; |
| 1385 } else if (node is AssignmentExpression) { | 1394 } else if (node is AssignmentExpression) { |
| 1386 return _getOffset(node.leftHandSide); | 1395 return _getOffset(node.leftHandSide); |
| 1387 } else if (node is PropertyAccess) { | 1396 } else if (node is PropertyAccess) { |
| 1388 return node.propertyName.offset; | 1397 return node.propertyName.offset; |
| 1389 } else if (node is IsExpression) { | 1398 } else if (node is IsExpression) { |
| 1390 return node.isOperator.offset; | 1399 return node.isOperator.offset; |
| 1400 } else if (node is AsExpression) { |
| 1401 return node.asOperator.offset; |
| 1391 } else if (node is StringLiteral) { | 1402 } else if (node is StringLiteral) { |
| 1392 // Use a catch-all for StringInterpolation and AdjacentStrings: | 1403 // Use a catch-all for StringInterpolation and AdjacentStrings: |
| 1393 // the debugger stops at the end. | 1404 // the debugger stops at the end. |
| 1394 return node.end; | 1405 return node.end; |
| 1406 } else if (node is IndexExpression) { |
| 1407 return node.leftBracket.offset; |
| 1395 } | 1408 } |
| 1396 return node.offset; | 1409 return node.offset; |
| 1397 } | 1410 } |
| 1398 | 1411 |
| 1399 Accessor buildLeftHandValue(Expression node) { | 1412 Accessor buildLeftHandValue(Expression node) { |
| 1400 var result = node.accept(this); | 1413 var result = node.accept(this); |
| 1401 if (result is Accessor) { | 1414 if (result is Accessor) { |
| 1402 return result; | 1415 return result; |
| 1403 } else { | 1416 } else { |
| 1404 return new ReadOnlyAccessor(result); | 1417 return new ReadOnlyAccessor(result); |
| (...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1865 scope.areArgumentsCompatible(element, arguments)) { | 1878 scope.areArgumentsCompatible(element, arguments)) { |
| 1866 return new ast.ConstructorInvocation(constructor, arguments, | 1879 return new ast.ConstructorInvocation(constructor, arguments, |
| 1867 isConst: node.isConst); | 1880 isConst: node.isConst); |
| 1868 } else { | 1881 } else { |
| 1869 return noSuchMethodError(); | 1882 return noSuchMethodError(); |
| 1870 } | 1883 } |
| 1871 } | 1884 } |
| 1872 | 1885 |
| 1873 ast.Expression visitIsExpression(IsExpression node) { | 1886 ast.Expression visitIsExpression(IsExpression node) { |
| 1874 if (node.notOperator != null) { | 1887 if (node.notOperator != null) { |
| 1888 // Put offset on the IsExpression for "is!" cases: |
| 1889 // As it is wrapped in a not, it won't get an offset otherwise. |
| 1875 return new ast.Not(new ast.IsExpression( | 1890 return new ast.Not(new ast.IsExpression( |
| 1876 build(node.expression), scope.buildTypeAnnotation(node.type))); | 1891 build(node.expression), scope.buildTypeAnnotation(node.type)) |
| 1892 ..fileOffset = _getOffset(node)); |
| 1877 } else { | 1893 } else { |
| 1878 return new ast.IsExpression( | 1894 return new ast.IsExpression( |
| 1879 build(node.expression), scope.buildTypeAnnotation(node.type)); | 1895 build(node.expression), scope.buildTypeAnnotation(node.type)); |
| 1880 } | 1896 } |
| 1881 } | 1897 } |
| 1882 | 1898 |
| 1883 /// Emit a method invocation, either as a direct call `o.f(x)` or decomposed | 1899 /// Emit a method invocation, either as a direct call `o.f(x)` or decomposed |
| 1884 /// into a getter and function invocation `o.f.call(x)`. | 1900 /// into a getter and function invocation `o.f.call(x)`. |
| 1885 ast.Expression buildDecomposableMethodInvocation(ast.Expression receiver, | 1901 ast.Expression buildDecomposableMethodInvocation(ast.Expression receiver, |
| 1886 ast.Name name, ast.Arguments arguments, Element targetElement) { | 1902 ast.Name name, ast.Arguments arguments, Element targetElement) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1915 scope.buildName(node.methodName), | 1931 scope.buildName(node.methodName), |
| 1916 buildArgumentsForInvocation(node), | 1932 buildArgumentsForInvocation(node), |
| 1917 element); | 1933 element); |
| 1918 } else if (target is SuperExpression) { | 1934 } else if (target is SuperExpression) { |
| 1919 scope.addTransformerFlag(TransformerFlag.superCalls); | 1935 scope.addTransformerFlag(TransformerFlag.superCalls); |
| 1920 return new ast.SuperMethodInvocation( | 1936 return new ast.SuperMethodInvocation( |
| 1921 scope.buildName(node.methodName), | 1937 scope.buildName(node.methodName), |
| 1922 buildArgumentsForInvocation(node), | 1938 buildArgumentsForInvocation(node), |
| 1923 scope.resolveConcreteMethod(element)); | 1939 scope.resolveConcreteMethod(element)); |
| 1924 } else if (isLocal(element)) { | 1940 } else if (isLocal(element)) { |
| 1941 // Set the offset directly: Normally the offset is at the start of the |
| 1942 // method, but in this case, because we insert a '.call', we want it at |
| 1943 // the end instead. |
| 1925 return new ast.MethodInvocation( | 1944 return new ast.MethodInvocation( |
| 1926 new ast.VariableGet(scope.getVariableReference(element)), | 1945 new ast.VariableGet(scope.getVariableReference(element)), |
| 1927 callName, | 1946 callName, |
| 1928 buildArgumentsForInvocation(node), | 1947 buildArgumentsForInvocation(node), |
| 1929 scope.resolveInterfaceFunctionCall(element)); | 1948 scope.resolveInterfaceFunctionCall(element)) |
| 1949 ..fileOffset = node.methodName.end; |
| 1930 } else if (isStaticMethod(element)) { | 1950 } else if (isStaticMethod(element)) { |
| 1931 var method = scope.resolveConcreteMethod(element); | 1951 var method = scope.resolveConcreteMethod(element); |
| 1932 var arguments = buildArgumentsForInvocation(node); | 1952 var arguments = buildArgumentsForInvocation(node); |
| 1933 if (method == null || !scope.areArgumentsCompatible(element, arguments)) { | 1953 if (method == null || !scope.areArgumentsCompatible(element, arguments)) { |
| 1934 return scope.buildThrowNoSuchMethodError( | 1954 return scope.buildThrowNoSuchMethodError( |
| 1935 new ast.NullLiteral(), node.methodName.name, arguments, | 1955 new ast.NullLiteral(), node.methodName.name, arguments, |
| 1936 candidateTarget: element); | 1956 candidateTarget: element); |
| 1937 } | 1957 } |
| 1938 return new ast.StaticInvocation(method, arguments); | 1958 return new ast.StaticInvocation(method, arguments); |
| 1939 } else if (isStaticVariableOrGetter(element)) { | 1959 } else if (isStaticVariableOrGetter(element)) { |
| 1940 var method = scope.resolveConcreteGet(element, null); | 1960 var method = scope.resolveConcreteGet(element, null); |
| 1941 if (method == null) { | 1961 if (method == null) { |
| 1942 return scope.buildThrowNoSuchMethodError( | 1962 return scope.buildThrowNoSuchMethodError( |
| 1943 new ast.NullLiteral(), node.methodName.name, new ast.Arguments([]), | 1963 new ast.NullLiteral(), node.methodName.name, new ast.Arguments([]), |
| 1944 candidateTarget: element); | 1964 candidateTarget: element); |
| 1945 } | 1965 } |
| 1966 // Set the offset directly: Normally the offset is at the start of the |
| 1967 // method, but in this case, because we insert a '.call', we want it at |
| 1968 // the end instead. |
| 1946 return new ast.MethodInvocation( | 1969 return new ast.MethodInvocation( |
| 1947 new ast.StaticGet(method), | 1970 new ast.StaticGet(method), |
| 1948 callName, | 1971 callName, |
| 1949 buildArgumentsForInvocation(node), | 1972 buildArgumentsForInvocation(node), |
| 1950 scope.resolveInterfaceFunctionCall(element)); | 1973 scope.resolveInterfaceFunctionCall(element)) |
| 1974 ..fileOffset = node.methodName.end; |
| 1951 } else if (target == null && !scope.allowThis || | 1975 } else if (target == null && !scope.allowThis || |
| 1952 target is Identifier && target.staticElement is ClassElement || | 1976 target is Identifier && target.staticElement is ClassElement || |
| 1953 target is Identifier && target.staticElement is PrefixElement) { | 1977 target is Identifier && target.staticElement is PrefixElement) { |
| 1954 return scope.buildThrowNoSuchMethodError(new ast.NullLiteral(), | 1978 return scope.buildThrowNoSuchMethodError(new ast.NullLiteral(), |
| 1955 node.methodName.name, buildArgumentsForInvocation(node), | 1979 node.methodName.name, buildArgumentsForInvocation(node), |
| 1956 candidateTarget: element); | 1980 candidateTarget: element); |
| 1957 } else if (target == null) { | 1981 } else if (target == null) { |
| 1958 return buildDecomposableMethodInvocation( | 1982 return buildDecomposableMethodInvocation( |
| 1959 scope.buildThis(), | 1983 scope.buildThis(), |
| 1960 scope.buildName(node.methodName), | 1984 scope.buildName(node.methodName), |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1999 } | 2023 } |
| 2000 | 2024 |
| 2001 ast.Expression visitPostfixExpression(PostfixExpression node) { | 2025 ast.Expression visitPostfixExpression(PostfixExpression node) { |
| 2002 String operator = node.operator.value(); | 2026 String operator = node.operator.value(); |
| 2003 switch (operator) { | 2027 switch (operator) { |
| 2004 case '++': | 2028 case '++': |
| 2005 case '--': | 2029 case '--': |
| 2006 var leftHand = buildLeftHandValue(node.operand); | 2030 var leftHand = buildLeftHandValue(node.operand); |
| 2007 var binaryOperator = new ast.Name(operator[0]); | 2031 var binaryOperator = new ast.Name(operator[0]); |
| 2008 return leftHand.buildPostfixIncrement(binaryOperator, | 2032 return leftHand.buildPostfixIncrement(binaryOperator, |
| 2009 offset: node.offset, | 2033 offset: node.operator.offset, |
| 2010 voidContext: isInVoidContext(node), | 2034 voidContext: isInVoidContext(node), |
| 2011 interfaceTarget: scope.resolveInterfaceMethod(node.staticElement)); | 2035 interfaceTarget: scope.resolveInterfaceMethod(node.staticElement)); |
| 2012 | 2036 |
| 2013 default: | 2037 default: |
| 2014 return scope.internalError('Invalid postfix operator $operator'); | 2038 return scope.internalError('Invalid postfix operator $operator'); |
| 2015 } | 2039 } |
| 2016 } | 2040 } |
| 2017 | 2041 |
| 2018 ast.Expression visitPrefixExpression(PrefixExpression node) { | 2042 ast.Expression visitPrefixExpression(PrefixExpression node) { |
| 2019 String operator = node.operator.value(); | 2043 String operator = node.operator.value(); |
| (...skipping 1026 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3046 if (list[i - 1].compareTo(item) == 0) { | 3070 if (list[i - 1].compareTo(item) == 0) { |
| 3047 ++deleted; | 3071 ++deleted; |
| 3048 } else if (deleted > 0) { | 3072 } else if (deleted > 0) { |
| 3049 list[i - deleted] = item; | 3073 list[i - deleted] = item; |
| 3050 } | 3074 } |
| 3051 } | 3075 } |
| 3052 if (deleted > 0) { | 3076 if (deleted > 0) { |
| 3053 list.length -= deleted; | 3077 list.length -= deleted; |
| 3054 } | 3078 } |
| 3055 } | 3079 } |
| OLD | NEW |