Unit.java revision 3827:44bdefe64114
190075Sobrien/* 290075Sobrien * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 390075Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 490075Sobrien * 590075Sobrien * This code is free software; you can redistribute it and/or modify it 690075Sobrien * under the terms of the GNU General Public License version 2 only, as 790075Sobrien * published by the Free Software Foundation. Oracle designates this 890075Sobrien * particular file as subject to the "Classpath" exception as provided 990075Sobrien * by Oracle in the LICENSE file that accompanied this code. 1090075Sobrien * 1190075Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT 1290075Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1390075Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1490075Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1590075Sobrien * accompanied this code). 1690075Sobrien * 1790075Sobrien * You should have received a copy of the GNU General Public License version 1890075Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 1990075Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2090075Sobrien * 2190075Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2290075Sobrien * or visit www.oracle.com if you need additional information or have any 2390075Sobrien * questions. 2490075Sobrien */ 2590075Sobrien 2690075Sobrienpackage jdk.jshell; 2790075Sobrien 2890075Sobrienimport java.util.ArrayList; 2990075Sobrienimport java.util.Collection; 3090075Sobrienimport java.util.Collections; 3190075Sobrienimport java.util.LinkedHashSet; 3290075Sobrienimport java.util.List; 3390075Sobrienimport java.util.Set; 3490075Sobrienimport java.util.stream.Stream; 3590075Sobrienimport jdk.jshell.ClassTracker.ClassInfo; 3690075Sobrienimport jdk.jshell.Snippet.Kind; 3790075Sobrienimport jdk.jshell.Snippet.Status; 3890075Sobrienimport jdk.jshell.Snippet.SubKind; 3990075Sobrienimport jdk.jshell.TaskFactory.AnalyzeTask; 4090075Sobrienimport jdk.jshell.TaskFactory.CompileTask; 4190075Sobrienimport jdk.jshell.spi.ExecutionControl.ClassBytecodes; 4290075Sobrienimport jdk.jshell.spi.ExecutionControl.ClassInstallException; 4390075Sobrienimport jdk.jshell.spi.ExecutionControl.EngineTerminationException; 4490075Sobrienimport jdk.jshell.spi.ExecutionControl.NotImplementedException; 4590075Sobrienimport static java.util.stream.Collectors.toList; 4690075Sobrienimport static java.util.stream.Collectors.toSet; 4790075Sobrienimport static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT; 4890075Sobrienimport static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN; 4990075Sobrienimport static jdk.jshell.Snippet.Status.OVERWRITTEN; 5090075Sobrienimport static jdk.jshell.Snippet.Status.RECOVERABLE_DEFINED; 5190075Sobrienimport static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED; 5290075Sobrienimport static jdk.jshell.Snippet.Status.REJECTED; 5390075Sobrienimport static jdk.jshell.Snippet.Status.VALID; 5490075Sobrienimport static jdk.jshell.Util.PARSED_LOCALE; 5590075Sobrienimport static jdk.jshell.Util.expunge; 5690075Sobrien 5790075Sobrien/** 5890075Sobrien * Tracks the compilation and load of a new or updated snippet. 5990075Sobrien * @author Robert Field 6090075Sobrien */ 6190075Sobrienfinal class Unit { 6296263Sobrien 6396263Sobrien private final JShell state; 6496263Sobrien private final Snippet si; 6590075Sobrien private final Snippet siOld; 6690075Sobrien private final boolean isDependency; 6790075Sobrien private final boolean isNew; 6890075Sobrien private final Snippet causalSnippet; 6990075Sobrien private final DiagList generatedDiagnostics; 7090075Sobrien 7190075Sobrien private int seq; 7290075Sobrien private String classNameInitial; 7390075Sobrien private Wrap activeGuts; 7490075Sobrien private Status status; 7590075Sobrien private Status prevStatus; 7690075Sobrien private boolean signatureChanged; 7790075Sobrien private DiagList compilationDiagnostics; 7890075Sobrien private DiagList recompilationDiagnostics = null; 7990075Sobrien private List<String> unresolved; 8090075Sobrien private SnippetEvent replaceOldEvent; 8190075Sobrien private List<SnippetEvent> secondaryEvents; 8290075Sobrien private boolean isAttemptingCorral; 8390075Sobrien private List<ClassInfo> toRedefine; 8490075Sobrien private boolean dependenciesNeeded; 8590075Sobrien 8690075Sobrien Unit(JShell state, Snippet si, Snippet causalSnippet, 8790075Sobrien DiagList generatedDiagnostics) { 8890075Sobrien this.state = state; 8990075Sobrien this.si = si; 9090075Sobrien this.isDependency = causalSnippet != null; 9190075Sobrien this.siOld = isDependency 9290075Sobrien ? si 9390075Sobrien : state.maps.getSnippet(si.key()); 9490075Sobrien this.isNew = siOld == null; 9590075Sobrien this.causalSnippet = causalSnippet; 9690075Sobrien this.generatedDiagnostics = generatedDiagnostics; 9790075Sobrien 9890075Sobrien this.seq = isNew? 0 : siOld.sequenceNumber(); 9990075Sobrien this.classNameInitial = isNew? "<none>" : siOld.className(); 10090075Sobrien this.prevStatus = (isNew || isDependency) 10190075Sobrien ? si.status() 10290075Sobrien : siOld.status(); 10390075Sobrien si.setSequenceNumber(seq); 10490075Sobrien } 10590075Sobrien 10690075Sobrien // Drop entry 10790075Sobrien Unit(JShell state, Snippet si) { 10890075Sobrien this.state = state; 10990075Sobrien this.si = si; 11090075Sobrien this.siOld = null; 11190075Sobrien this.isDependency = false; 11290075Sobrien this.isNew = false; 11390075Sobrien this.causalSnippet = null; 11490075Sobrien this.generatedDiagnostics = new DiagList(); 11590075Sobrien this.prevStatus = si.status(); 11690075Sobrien si.setDropped(); 11790075Sobrien this.status = si.status(); 11890075Sobrien } 11990075Sobrien 12090075Sobrien @Override 12190075Sobrien public int hashCode() { 12290075Sobrien return si.hashCode(); 12390075Sobrien } 12490075Sobrien 12590075Sobrien @Override 12690075Sobrien public boolean equals(Object o) { 12790075Sobrien return (o instanceof Unit) 12890075Sobrien ? si.equals(((Unit) o).si) 12990075Sobrien : false; 13090075Sobrien } 13190075Sobrien 13290075Sobrien Snippet snippet() { 13390075Sobrien return si; 13490075Sobrien } 13590075Sobrien 13690075Sobrien boolean isDependency() { 13790075Sobrien return isDependency; 13890075Sobrien } 13990075Sobrien 14090075Sobrien void initialize() { 14190075Sobrien isAttemptingCorral = false; 14290075Sobrien dependenciesNeeded = false; 14390075Sobrien toRedefine = null; // assure NPE if classToLoad not called 14490075Sobrien activeGuts = si.guts(); 14590075Sobrien markOldDeclarationOverwritten(); 14690075Sobrien } 14790075Sobrien 14896263Sobrien // Set the outer wrap of our Snippet 14996263Sobrien void setWrap(Collection<Unit> exceptUnit, Collection<Unit> plusUnfiltered) { 15096263Sobrien if (isImport()) { 15196263Sobrien si.setOuterWrap(state.outerMap.wrapImport(activeGuts, si)); 15290075Sobrien } else { 15390075Sobrien // Collect Units for be wrapped together. Just this except for overloaded methods 15490075Sobrien List<Unit> units; 15590075Sobrien if (snippet().kind() == Kind.METHOD) { 15690075Sobrien String name = ((MethodSnippet) snippet()).name(); 15790075Sobrien units = plusUnfiltered.stream() 15890075Sobrien .filter(u -> u.snippet().kind() == Kind.METHOD && 15990075Sobrien ((MethodSnippet) u.snippet()).name().equals(name)) 16090075Sobrien .collect(toList()); 16190075Sobrien } else { 16290075Sobrien units = Collections.singletonList(this); 16390075Sobrien } 16490075Sobrien // Keys to exclude from imports 16590075Sobrien Set<Key> except = exceptUnit.stream() 16690075Sobrien .map(u -> u.snippet().key()) 16790075Sobrien .collect(toSet()); 16890075Sobrien // Snippets to add to imports 16990075Sobrien Collection<Snippet> plus = plusUnfiltered.stream() 17090075Sobrien .filter(u -> !units.contains(u)) 17190075Sobrien .map(Unit::snippet) 17290075Sobrien .collect(toList()); 17390075Sobrien // Snippets to wrap in an outer 17490075Sobrien List<Snippet> snippets = units.stream() 17590075Sobrien .map(Unit::snippet) 17690075Sobrien .collect(toList()); 17790075Sobrien // Snippet wraps to wrap in an outer 17890075Sobrien List<Wrap> wraps = units.stream() 17990075Sobrien .map(u -> u.activeGuts) 18090075Sobrien .collect(toList()); 18190075Sobrien // Set the outer wrap for this snippet 18290075Sobrien si.setOuterWrap(state.outerMap.wrapInClass(except, plus, snippets, wraps)); 18390075Sobrien } 18490075Sobrien } 18590075Sobrien 18690075Sobrien void setDiagnostics(AnalyzeTask ct) { 18790075Sobrien setDiagnostics(ct.getDiagnostics().ofUnit(this)); 18890075Sobrien } 18990075Sobrien 19090075Sobrien void setDiagnostics(DiagList diags) { 19190075Sobrien compilationDiagnostics = diags; 19290075Sobrien UnresolvedExtractor ue = new UnresolvedExtractor(diags); 19390075Sobrien unresolved = ue.unresolved(); 19490075Sobrien state.debug(DBG_GEN, "++setCompilationInfo() %s\n%s\n-- diags: %s\n", 19590075Sobrien si, si.outerWrap().wrapped(), diags); 19690075Sobrien } 19790075Sobrien 19890075Sobrien private boolean isRecoverable() { 19990075Sobrien // Unit failed, use corralling if it is defined on this Snippet, 20090075Sobrien // and either all the errors are resolution errors or this is a 20190075Sobrien // redeclare of an existing method 20290075Sobrien return compilationDiagnostics.hasErrors() 20390075Sobrien && si instanceof DeclarationSnippet 20490075Sobrien && (isDependency() 20590075Sobrien || (si.subKind() != SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND 20690075Sobrien && compilationDiagnostics.hasResolutionErrorsAndNoOthers())); 20790075Sobrien } 20890075Sobrien 20990075Sobrien /** 21090075Sobrien * If it meets the conditions for corralling, install the corralled wrap 21190075Sobrien * @return true is the corralled wrap was installed 21290075Sobrien */ 21390075Sobrien boolean corralIfNeeded(Collection<Unit> working) { 21490075Sobrien if (isRecoverable() 21590075Sobrien && si.corralled() != null) { 21690075Sobrien activeGuts = si.corralled(); 21790075Sobrien setWrap(working, working); 21890075Sobrien return isAttemptingCorral = true; 21990075Sobrien } 22090075Sobrien return isAttemptingCorral = false; 22190075Sobrien } 22290075Sobrien 22390075Sobrien void setCorralledDiagnostics(AnalyzeTask cct) { 22490075Sobrien // set corralled diagnostics, but don't reset unresolved 22590075Sobrien recompilationDiagnostics = cct.getDiagnostics().ofUnit(this); 22690075Sobrien state.debug(DBG_GEN, "++recomp %s\n%s\n-- diags: %s\n", 22790075Sobrien si, si.outerWrap().wrapped(), recompilationDiagnostics); 22890075Sobrien } 22990075Sobrien 23090075Sobrien boolean smashingErrorDiagnostics(CompileTask ct) { 23190075Sobrien if (isDefined()) { 23290075Sobrien // set corralled diagnostics, but don't reset unresolved 23390075Sobrien DiagList dl = ct.getDiagnostics().ofUnit(this); 23490075Sobrien if (dl.hasErrors()) { 23590075Sobrien setDiagnostics(dl); 23690075Sobrien status = RECOVERABLE_NOT_DEFINED; 23790075Sobrien // overwrite orginal bytes 23890075Sobrien state.debug(DBG_GEN, "++smashingErrorDiagnostics %s\n%s\n-- diags: %s\n", 23990075Sobrien si, si.outerWrap().wrapped(), dl); 24090075Sobrien return true; 24190075Sobrien } 24290075Sobrien } 24390075Sobrien return false; 24490075Sobrien } 24590075Sobrien 24690075Sobrien void setStatus(AnalyzeTask at) { 24790075Sobrien if (!compilationDiagnostics.hasErrors()) { 24890075Sobrien status = VALID; 24990075Sobrien } else if (isRecoverable()) { 25090075Sobrien if (isAttemptingCorral && !recompilationDiagnostics.hasErrors()) { 25190075Sobrien status = RECOVERABLE_DEFINED; 25290075Sobrien } else { 25390075Sobrien status = RECOVERABLE_NOT_DEFINED; 25490075Sobrien } 25590075Sobrien } else { 25690075Sobrien status = REJECTED; 25790075Sobrien } 25890075Sobrien checkForOverwrite(at); 25990075Sobrien 26090075Sobrien state.debug(DBG_GEN, "setStatus() %s - status: %s\n", 26190075Sobrien si, status); 26290075Sobrien } 26390075Sobrien 26490075Sobrien boolean isDefined() { 26590075Sobrien return status.isDefined(); 26690075Sobrien } 26790075Sobrien 26890075Sobrien /** 26990075Sobrien * Process the class information from the last compile. Requires loading of 27090075Sobrien * returned list. 27190075Sobrien * 27290075Sobrien * @return the list of classes to load 27390075Sobrien */ 27490075Sobrien Stream<ClassBytecodes> classesToLoad(List<String> classnames) { 27590075Sobrien toRedefine = new ArrayList<>(); 27690075Sobrien List<ClassBytecodes> toLoad = new ArrayList<>(); 27790075Sobrien if (status.isDefined() && !isImport()) { 27890075Sobrien // Classes should only be loaded/redefined if the compile left them 27990075Sobrien // in a defined state. Imports do not have code and are not loaded. 28090075Sobrien for (String cn : classnames) { 28190075Sobrien ClassInfo ci = state.classTracker.get(cn); 28290075Sobrien if (ci.isLoaded()) { 28390075Sobrien if (ci.isCurrent()) { 28490075Sobrien // nothing to do 28590075Sobrien } else { 28690075Sobrien toRedefine.add(ci); 28790075Sobrien } 28890075Sobrien } else { 28990075Sobrien // If not loaded, add to the list of classes to load. 29090075Sobrien toLoad.add(ci.toClassBytecodes()); 29190075Sobrien dependenciesNeeded = true; 29290075Sobrien } 29390075Sobrien } 29490075Sobrien } 29590075Sobrien return toLoad.stream(); 29690075Sobrien } 29790075Sobrien 29890075Sobrien /** 29990075Sobrien * Redefine classes needing redefine. classesToLoad() must be called first. 30090075Sobrien * 30190075Sobrien * @return true if all redefines succeeded (can be vacuously true) 30290075Sobrien */ 30390075Sobrien boolean doRedefines() { 30490075Sobrien if (toRedefine.isEmpty()) { 30590075Sobrien return true; 30690075Sobrien } 30790075Sobrien ClassBytecodes[] cbcs = toRedefine.stream() 30890075Sobrien .map(ClassInfo::toClassBytecodes) 30990075Sobrien .toArray(ClassBytecodes[]::new); 31090075Sobrien try { 31190075Sobrien state.executionControl().redefine(cbcs); 31290075Sobrien state.classTracker.markLoaded(cbcs); 31390075Sobrien return true; 31490075Sobrien } catch (ClassInstallException ex) { 31590075Sobrien state.classTracker.markLoaded(cbcs, ex.installed()); 31690075Sobrien return false; 31790075Sobrien } catch (EngineTerminationException ex) { 31890075Sobrien state.closeDown(); 31990075Sobrien return false; 32090075Sobrien } catch (NotImplementedException ex) { 32190075Sobrien return false; 32290075Sobrien } 32390075Sobrien } 32490075Sobrien 32590075Sobrien void markForReplacement() { 32690075Sobrien // increment for replace class wrapper 32796263Sobrien si.setSequenceNumber(++seq); 32890075Sobrien } 32990075Sobrien 33090075Sobrien private boolean isImport() { 33190075Sobrien return si.kind() == Kind.IMPORT; 33290075Sobrien } 33390075Sobrien 33490075Sobrien private boolean sigChanged() { 33590075Sobrien return (status.isDefined() != prevStatus.isDefined()) 33690075Sobrien || (status.isDefined() && !si.className().equals(classNameInitial)) 33790075Sobrien || signatureChanged; 33890075Sobrien } 33990075Sobrien 34090075Sobrien Stream<Unit> effectedDependents() { 34190075Sobrien //System.err.printf("effectedDependents sigChanged=%b dependenciesNeeded=%b status=%s\n", 34290075Sobrien // sigChanged(), dependenciesNeeded, status); 34390075Sobrien return sigChanged() || dependenciesNeeded || status == RECOVERABLE_NOT_DEFINED 34490075Sobrien ? dependents() 34590075Sobrien : Stream.empty(); 34690075Sobrien } 34790075Sobrien 34890075Sobrien Stream<Unit> dependents() { 34990075Sobrien return state.maps.getDependents(si) 35090075Sobrien .stream() 35190075Sobrien .filter(xsi -> xsi != si && xsi.status().isActive()) 35290075Sobrien .map(xsi -> new Unit(state, xsi, si, new DiagList())); 35390075Sobrien } 35490075Sobrien 35590075Sobrien void finish() { 35690075Sobrien recordCompilation(); 35790075Sobrien state.maps.installSnippet(si); 35890075Sobrien } 35990075Sobrien 36090075Sobrien private void markOldDeclarationOverwritten() { 36190075Sobrien if (si != siOld && siOld != null && siOld.status().isActive()) { 36290075Sobrien // Mark the old declaraion as replaced 36390075Sobrien replaceOldEvent = new SnippetEvent(siOld, 36490075Sobrien siOld.status(), OVERWRITTEN, 36590075Sobrien false, si, null, null); 36690075Sobrien siOld.setOverwritten(); 36790075Sobrien } 36890075Sobrien } 36990075Sobrien 37090075Sobrien private DiagList computeDiagnostics() { 37190075Sobrien DiagList diagnostics = new DiagList(); 37290075Sobrien DiagList diags = compilationDiagnostics; 37390075Sobrien if (status == RECOVERABLE_DEFINED || status == RECOVERABLE_NOT_DEFINED) { 37490075Sobrien UnresolvedExtractor ue = new UnresolvedExtractor(diags); 37590075Sobrien diagnostics.addAll(ue.otherAll()); 37690075Sobrien } else { 37790075Sobrien unresolved = Collections.emptyList(); 37890075Sobrien diagnostics.addAll(diags); 37990075Sobrien } 38090075Sobrien diagnostics.addAll(generatedDiagnostics); 38190075Sobrien return diagnostics; 38290075Sobrien } 38390075Sobrien 38490075Sobrien private void recordCompilation() { 38590075Sobrien state.maps.mapDependencies(si); 38690075Sobrien DiagList diags = computeDiagnostics(); 38790075Sobrien si.setCompilationStatus(status, unresolved, diags); 38890075Sobrien state.debug(DBG_GEN, "recordCompilation: %s -- status %s, unresolved %s\n", 38990075Sobrien si, status, unresolved); 39090075Sobrien } 39190075Sobrien 39290075Sobrien private void checkForOverwrite(AnalyzeTask at) { 39390075Sobrien secondaryEvents = new ArrayList<>(); 39490075Sobrien if (replaceOldEvent != null) secondaryEvents.add(replaceOldEvent); 39590075Sobrien 39690075Sobrien // Defined methods can overwrite methods of other (equivalent) snippets 39790075Sobrien if (isNew && si.kind() == Kind.METHOD && status.isDefined()) { 39890075Sobrien MethodSnippet msi = (MethodSnippet)si; 39990075Sobrien String oqpt = msi.qualifiedParameterTypes(); 40090075Sobrien String nqpt = computeQualifiedParameterTypes(at, msi); 40190075Sobrien if (!nqpt.equals(oqpt)) { 40290075Sobrien msi.setQualifiedParamaterTypes(nqpt); 40390075Sobrien Status overwrittenStatus = overwriteMatchingMethod(msi); 40490075Sobrien if (overwrittenStatus != null) { 405 prevStatus = overwrittenStatus; 406 signatureChanged = true; 407 } 408 } 409 } 410 } 411 412 // Check if there is a method whose user-declared parameter types are 413 // different (and thus has a different snippet) but whose compiled parameter 414 // types are the same. if so, consider it an overwrite replacement. 415 private Status overwriteMatchingMethod(MethodSnippet msi) { 416 String qpt = msi.qualifiedParameterTypes(); 417 List<MethodSnippet> matching = state.methods() 418 .filter(sn -> 419 sn != null 420 && sn != msi 421 && sn.status().isActive() 422 && sn.name().equals(msi.name()) 423 && qpt.equals(sn.qualifiedParameterTypes())) 424 .collect(toList()); 425 426 // Look through all methods for a method of the same name, with the 427 // same computed qualified parameter types 428 Status overwrittenStatus = null; 429 for (MethodSnippet sn : matching) { 430 overwrittenStatus = sn.status(); 431 SnippetEvent se = new SnippetEvent( 432 sn, overwrittenStatus, OVERWRITTEN, 433 false, msi, null, null); 434 sn.setOverwritten(); 435 secondaryEvents.add(se); 436 state.debug(DBG_EVNT, 437 "Overwrite event #%d -- key: %s before: %s status: %s sig: %b cause: %s\n", 438 secondaryEvents.size(), se.snippet(), se.previousStatus(), 439 se.status(), se.isSignatureChange(), se.causeSnippet()); 440 } 441 return overwrittenStatus; 442 } 443 444 private String computeQualifiedParameterTypes(AnalyzeTask at, MethodSnippet msi) { 445 String rawSig = TreeDissector.createBySnippet(at, msi).typeOfMethod(msi); 446 String signature = expunge(rawSig); 447 int paren = signature.lastIndexOf(')'); 448 449 // Extract the parameter type string from the method signature, 450 // if method did not compile use the user-supplied parameter types 451 return paren >= 0 452 ? signature.substring(0, paren + 1) 453 : msi.parameterTypes(); 454 } 455 456 SnippetEvent event(String value, JShellException exception) { 457 boolean wasSignatureChanged = sigChanged(); 458 state.debug(DBG_EVNT, "Snippet: %s id: %s before: %s status: %s sig: %b cause: %s\n", 459 si, si.id(), prevStatus, si.status(), wasSignatureChanged, causalSnippet); 460 return new SnippetEvent(si, prevStatus, si.status(), 461 wasSignatureChanged, causalSnippet, value, exception); 462 } 463 464 List<SnippetEvent> secondaryEvents() { 465 return secondaryEvents==null 466 ? Collections.emptyList() 467 : secondaryEvents; 468 } 469 470 @Override 471 public String toString() { 472 return "Unit(" + si.name() + ")"; 473 } 474 475 /** 476 * Separate out the unresolvedDependencies errors from both the other 477 * corralling errors and the overall errors. 478 */ 479 private static class UnresolvedExtractor { 480 481 private static final String RESOLVE_ERROR_SYMBOL = "symbol:"; 482 private static final String RESOLVE_ERROR_LOCATION = "location:"; 483 484 //TODO extract from tree instead -- note: internationalization 485 private final Set<String> unresolved = new LinkedHashSet<>(); 486 private final DiagList otherErrors = new DiagList(); 487 private final DiagList otherAll = new DiagList(); 488 489 UnresolvedExtractor(DiagList diags) { 490 for (Diag diag : diags) { 491 if (diag.isError()) { 492 if (diag.isResolutionError()) { 493 String m = diag.getMessage(PARSED_LOCALE); 494 int symPos = m.indexOf(RESOLVE_ERROR_SYMBOL); 495 if (symPos >= 0) { 496 m = m.substring(symPos + RESOLVE_ERROR_SYMBOL.length()); 497 int symLoc = m.indexOf(RESOLVE_ERROR_LOCATION); 498 if (symLoc >= 0) { 499 m = m.substring(0, symLoc); 500 } 501 m = m.trim(); 502 unresolved.add(m); 503 continue; 504 } 505 } 506 otherErrors.add(diag); 507 } 508 otherAll.add(diag); 509 } 510 } 511 512 DiagList otherCorralledErrors() { 513 return otherErrors; 514 } 515 516 DiagList otherAll() { 517 return otherAll; 518 } 519 520 List<String> unresolved() { 521 return new ArrayList<>(unresolved); 522 } 523 } 524} 525