| package org.chromium.devtools.jsdoc; |
| |
| import com.google.javascript.jscomp.Compiler; |
| import com.google.javascript.jscomp.NodeTraversal; |
| import com.google.javascript.jscomp.parsing.Config; |
| import com.google.javascript.jscomp.parsing.Config.JsDocParsing; |
| import com.google.javascript.jscomp.parsing.Config.LanguageMode; |
| import com.google.javascript.jscomp.parsing.Config.StrictMode; |
| import com.google.javascript.jscomp.parsing.Config.RunMode; |
| import com.google.javascript.jscomp.parsing.ParserRunner; |
| import com.google.javascript.rhino.ErrorReporter; |
| import com.google.javascript.rhino.Node; |
| |
| import org.chromium.devtools.jsdoc.checks.ContextTrackingValidationCheck; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.FileSystems; |
| import java.nio.file.Files; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.Callable; |
| |
| public class FileCheckerCallable implements Callable<ValidatorContext> { |
| private static Set<String> EXTRA_ANNOTATIONS = |
| new HashSet<>(Arrays.asList("suppressReceiverCheck", "suppressGlobalPropertiesCheck")); |
| private final String fileName; |
| |
| public FileCheckerCallable(String fileName) { |
| this.fileName = fileName; |
| } |
| |
| @Override |
| public ValidatorContext call() { |
| try { |
| ValidatorContext context = new ValidatorContext(readScriptText(), fileName); |
| ValidationCheckDispatcher dispatcher = new ValidationCheckDispatcher(context); |
| dispatcher.registerCheck(new ContextTrackingValidationCheck()); |
| NodeTraversal.traverseEs6ScopeRoots(new Compiler(), parseScript(context), null, dispatcher, true); |
| return context; |
| } catch (FileNotFoundException e) { |
| logError("File not found: " + fileName); |
| } catch (IOException e) { |
| logError("Failed to read file " + fileName); |
| } |
| return null; |
| } |
| |
| private String readScriptText() throws IOException { |
| byte[] encoded = Files.readAllBytes(FileSystems.getDefault().getPath(fileName)); |
| String text = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(encoded)).toString(); |
| return text; |
| } |
| |
| private static Node parseScript(final ValidatorContext context) { |
| Config config = ParserRunner.createConfig(LanguageMode.ECMASCRIPT6, |
| JsDocParsing.INCLUDE_DESCRIPTIONS_WITH_WHITESPACE, RunMode.KEEP_GOING, |
| EXTRA_ANNOTATIONS, true, StrictMode.STRICT); |
| ErrorReporter errorReporter = new ErrorReporter() { |
| @Override |
| public void warning(String message, String sourceName, int line, int lineOffset) { |
| // Ignore. |
| } |
| |
| @Override |
| public void error(String message, String sourceName, int line, int lineOffset) { |
| logError("at " + sourceName + ":" + line + ":" + lineOffset); |
| } |
| }; |
| try { |
| return ParserRunner |
| .parse(context.sourceFile, context.sourceFile.getCode(), config, errorReporter) |
| .ast; |
| } catch (IOException e) { |
| // Does not happen with preloaded files. |
| return null; |
| } |
| } |
| |
| private static void logError(String message) { |
| System.err.println("ERROR: " + message); |
| } |
| |
| private static class ValidationCheckDispatcher extends DoDidVisitorAdapter { |
| private final List<ValidationCheck> checks = new ArrayList<>(2); |
| private final ValidatorContext context; |
| |
| public ValidationCheckDispatcher(ValidatorContext context) { |
| this.context = context; |
| } |
| |
| public void registerCheck(ValidationCheck check) { |
| check.setContext(context); |
| checks.add(check); |
| } |
| |
| @Override |
| public void doVisit(Node node) { |
| for (DoDidNodeVisitor visitor : checks) { |
| visitor.doVisit(node); |
| } |
| } |
| |
| @Override |
| public void didVisit(Node node) { |
| for (ValidationCheck check : checks) { |
| check.didVisit(node); |
| } |
| } |
| } |
| } |