1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.nashorn.api.tree;
  26 
  27 import java.io.File;
  28 import java.io.IOException;
  29 import java.io.PrintWriter;
  30 import java.io.Reader;
  31 import java.net.URL;
  32 import java.nio.file.Path;
  33 import java.util.Arrays;
  34 import java.util.Map;
  35 import java.util.Objects;
  36 import jdk.nashorn.api.scripting.NashornException;
  37 import jdk.nashorn.api.scripting.ScriptObjectMirror;
  38 import jdk.nashorn.internal.ir.FunctionNode;
  39 import jdk.nashorn.internal.runtime.Context;
  40 import jdk.nashorn.internal.runtime.ErrorManager;
  41 import jdk.nashorn.internal.runtime.JSType;
  42 import jdk.nashorn.internal.runtime.ParserException;
  43 import jdk.nashorn.internal.runtime.ScriptEnvironment;
  44 import jdk.nashorn.internal.runtime.Source;
  45 import jdk.nashorn.internal.runtime.options.Options;
  46 
  47 final class ParserImpl implements Parser {
  48 
  49     private final ScriptEnvironment env;
  50     private final boolean moduleMode;
  51 
  52     ParserImpl(final String... args) throws IllegalArgumentException {
  53         Objects.requireNonNull(args);
  54 
  55         // handle the parser specific "--es6-module" option
  56         boolean seenModuleOption = false;
  57         for (int idx = 0; idx < args.length; idx++) {
  58             final String opt = args[idx];
  59             if (opt.equals("--es6-module")) {
  60                 seenModuleOption = true;
  61                 /*
  62                  * Nashorn parser does not understand parser API specific
  63                  * option. This option implies --language=es6. So, we change
  64                  * the option to --language=es6. Note that if user specified
  65                  * --language=es6 explicitly, that is okay. Nashorn tolerates
  66                  * repeated options!
  67                  */
  68                 args[idx] = "--language=es6";
  69                 break;
  70             }
  71         }
  72         this.moduleMode = seenModuleOption;
  73 
  74         // append "--parse-only to signal to the Nashorn that it
  75         // is being used in "parse only" mode.
  76         final String[] newArgs = Arrays.copyOf(args, args.length + 1, String[].class);
  77         newArgs[args.length] = "--parse-only";
  78         final Options options = new Options("nashorn");
  79         options.process(newArgs);
  80         this.env = new ScriptEnvironment(options,
  81                 new PrintWriter(System.out), new PrintWriter(System.err));
  82     }
  83 
  84     @Override
  85     public CompilationUnitTree parse(final File file, final DiagnosticListener listener) throws IOException, NashornException {
  86         if (moduleMode) {
  87             return parseModule(file, listener);
  88         }
  89         final Source src = Source.sourceFor(Objects.requireNonNull(file).getName(), file);
  90         return translate(makeParser(src, listener).parse());
  91     }
  92 
  93     @Override
  94     public CompilationUnitTree parse(final Path path, final DiagnosticListener listener) throws IOException, NashornException {
  95         if (moduleMode) {
  96             return parseModule(path, listener);
  97         }
  98         final Source src = Source.sourceFor(Objects.requireNonNull(path).toString(), path);
  99         return translate(makeParser(src, listener).parse());
 100     }
 101 
 102     @Override
 103     public CompilationUnitTree parse(final URL url, final DiagnosticListener listener) throws IOException, NashornException {
 104         if (moduleMode) {
 105             return parseModule(url, listener);
 106         }
 107         final Source src = Source.sourceFor(url.toString(), url);
 108         return translate(makeParser(src, listener).parse());
 109     }
 110 
 111     @Override
 112     public CompilationUnitTree parse(final String name, final Reader reader, final DiagnosticListener listener) throws IOException, NashornException {
 113         if (moduleMode) {
 114             return parseModule(name, reader, listener);
 115         }
 116         final Source src = Source.sourceFor(Objects.requireNonNull(name), Objects.requireNonNull(reader));
 117         return translate(makeParser(src, listener).parse());
 118     }
 119 
 120     @Override
 121     public CompilationUnitTree parse(final String name, final String code, final DiagnosticListener listener) throws NashornException {
 122         if (moduleMode) {
 123             return parseModule(name, code, listener);
 124         }
 125         final Source src = Source.sourceFor(name, code);
 126         return translate(makeParser(src, listener).parse());
 127     }
 128 
 129     @Override
 130     public CompilationUnitTree parse(final ScriptObjectMirror scriptObj, final DiagnosticListener listener) throws NashornException {
 131         if (moduleMode) {
 132             return parseModule(scriptObj, listener);
 133         }
 134         final Map<?, ?> map = Objects.requireNonNull(scriptObj);
 135         if (map.containsKey("script") && map.containsKey("name")) {
 136             final String script = JSType.toString(map.get("script"));
 137             final String name = JSType.toString(map.get("name"));
 138             final Source src = Source.sourceFor(name, script);
 139             return translate(makeParser(src, listener).parse());
 140         } else {
 141             throw new IllegalArgumentException("can't find 'script' and 'name' properties");
 142         }
 143     }
 144 
 145     private CompilationUnitTree parseModule(final File file, final DiagnosticListener listener) throws IOException, NashornException {
 146         final Source src = Source.sourceFor(Objects.requireNonNull(file).getName(), file);
 147         return makeModule(src, listener);
 148     }
 149 
 150     private CompilationUnitTree parseModule(final Path path, final DiagnosticListener listener) throws IOException, NashornException {
 151         final Source src = Source.sourceFor(Objects.requireNonNull(path).toString(), path);
 152         return makeModule(src, listener);
 153     }
 154 
 155     private CompilationUnitTree parseModule(final URL url, final DiagnosticListener listener) throws IOException, NashornException {
 156         final Source src = Source.sourceFor(url.toString(), url);
 157         return makeModule(src, listener);
 158     }
 159 
 160     private CompilationUnitTree parseModule(final String name, final Reader reader, final DiagnosticListener listener) throws IOException, NashornException {
 161         final Source src = Source.sourceFor(Objects.requireNonNull(name), Objects.requireNonNull(reader));
 162         return makeModule(src, listener);
 163     }
 164 
 165     private CompilationUnitTree parseModule(final String name, final String code, final DiagnosticListener listener) throws NashornException {
 166         final Source src = Source.sourceFor(name, code);
 167         return makeModule(src, listener);
 168     }
 169 
 170     private CompilationUnitTree parseModule(final ScriptObjectMirror scriptObj, final DiagnosticListener listener) throws NashornException {
 171         final Map<?, ?> map = Objects.requireNonNull(scriptObj);
 172         if (map.containsKey("script") && map.containsKey("name")) {
 173             final String script = JSType.toString(map.get("script"));
 174             final String name = JSType.toString(map.get("name"));
 175             final Source src = Source.sourceFor(name, script);
 176             return makeModule(src, listener);
 177         } else {
 178             throw new IllegalArgumentException("can't find 'script' and 'name' properties");
 179         }
 180     }
 181 
 182     private CompilationUnitTree makeModule(final Source src, final DiagnosticListener listener) {
 183         final FunctionNode modFunc = makeParser(src, listener).parseModule(src.getName());
 184         return new IRTranslator().translate(modFunc);
 185     }
 186 
 187     private jdk.nashorn.internal.parser.Parser makeParser(final Source source, final DiagnosticListener listener) {
 188         final ErrorManager errMgr = listener != null ? new ListenerErrorManager(listener) : new Context.ThrowErrorManager();
 189         return new jdk.nashorn.internal.parser.Parser(env, source, errMgr);
 190     }
 191 
 192     private static class ListenerErrorManager extends ErrorManager {
 193 
 194         private final DiagnosticListener listener;
 195 
 196         ListenerErrorManager(final DiagnosticListener listener) {
 197             // null check
 198             listener.getClass();
 199             this.listener = listener;
 200         }
 201 
 202         @Override
 203         public void error(final String msg) {
 204             error(new ParserException(msg));
 205         }
 206 
 207         @Override
 208         public void error(final ParserException e) {
 209             listener.report(new DiagnosticImpl(e, Diagnostic.Kind.ERROR));
 210         }
 211 
 212         @Override
 213         public void warning(final String msg) {
 214             warning(new ParserException(msg));
 215         }
 216 
 217         @Override
 218         public void warning(final ParserException e) {
 219             listener.report(new DiagnosticImpl(e, Diagnostic.Kind.WARNING));
 220         }
 221     }
 222 
 223     private static CompilationUnitTree translate(final FunctionNode node) {
 224         return new IRTranslator().translate(node);
 225     }
 226 }