Package org.ringojs.tools

Source Code of org.ringojs.tools.RingoShell$JSCompletor

/*
*  Copyright 2008 Hannes Wallnoefer <hannes@helma.at>
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.ringojs.tools;

import jline.Completor;
import jline.ConsoleReader;
import jline.History;
import org.ringojs.engine.ModuleScope;
import org.ringojs.engine.ReloadableScript;
import org.ringojs.engine.RhinoEngine;
import org.ringojs.engine.RingoConfig;
import org.ringojs.engine.RingoWorker;
import org.ringojs.engine.ScriptError;
import org.ringojs.repository.Repository;
import org.mozilla.javascript.*;
import org.mozilla.javascript.tools.ToolErrorReporter;
import org.ringojs.repository.Resource;
import org.ringojs.repository.StringResource;
import org.ringojs.wrappers.ScriptableList;

import java.io.*;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.security.CodeSource;
import java.security.CodeSigner;

/**
* RingoShell is a simple interactive shell that provides the
* additional global functions implemented by Ringo.
*/
public class RingoShell {

    RingoConfig config;
    RhinoEngine engine;
    RingoWorker worker;
    Scriptable scope;
    boolean silent;
    File history;
    CodeSource codeSource = null;

    public RingoShell(RhinoEngine engine) throws IOException {
        this(engine, null, false);
    }

    public RingoShell(RhinoEngine engine, File history, boolean silent)
            throws IOException {
        this.config = engine.getConfig();
        this.engine = engine;
        this.history = history;
        this.worker = engine.getWorker();
        this.scope = engine.getShellScope(worker);
        this.silent = silent;
        // FIXME give shell code a trusted code source in case security is on
        if (config.isPolicyEnabled()) {
            Repository modules = config.getRingoHome().getChildRepository("modules");
            codeSource = new CodeSource(modules.getUrl(), (CodeSigner[])null);
        }
    }

    public void run() throws IOException {
        if (silent) {
            // bypass console if running with redirected stdin or stout
            runSilently();
            return;
        }
        preloadShellModule();
        ConsoleReader reader = new ConsoleReader();
        reader.setBellEnabled(false);
        // reader.setDebug(new PrintWriter(new FileWriter("jline.debug")));
        reader.addCompletor(new JSCompletor());
        if (history == null) {
            history = new File(System.getProperty("user.home"), ".ringo-history");
        }
        reader.setHistory(new History(history));
        PrintStream out = System.out;
        int lineno = 0;
        repl: while (true) {
            Context cx = engine.getContextFactory().enterContext(null);
            cx.setErrorReporter(new ToolErrorReporter(false, System.err));
            String source = "";
            String prompt = getPrompt();
            while (true) {
                String newline = reader.readLine(prompt);
                if (newline == null) {
                    // NULL input, if e.g. Ctrl-D was pressed
                    out.println();
                    out.flush();
                    break repl;
                }
                source = source + newline + "\n";
                lineno++;
                if (cx.stringIsCompilableUnit(source)) {
                    break;
                }
                prompt = getSecondaryPrompt();
            }
            try {
                Resource res = new StringResource("<stdin>", source, lineno);
                ReloadableScript script = new ReloadableScript(res, engine);
                Object result = worker.evaluateScript(cx, script, scope);

                printResult(result, out);
                lineno++;
                // trigger GC once in a while - if we run in non-interpreter mode
                // we generate a lot of classes to unload
                if (lineno % 10 == 0) {
                    System.gc();
                }
            } catch (Exception ex) {
                // TODO: should this print to System.err?
                printError(ex, out, config.isVerbose());
            } finally {
                Context.exit();
            }
        }
        System.exit(0);
    }

    protected String getPrompt() {
        return ">> ";
    }

    protected String getSecondaryPrompt() {
        return ".. ";
    }

    protected void printResult(Object result, PrintStream out) {
        try {
            worker.invoke("ringo/shell", "printResult", result);
        } catch (Exception x) {
            // Avoid printing out undefined or function definitions.
            if (result != Context.getUndefinedValue()) {
                out.println(Context.toString(result));
            }
            out.flush();
        }
    }

    protected void printError(Exception ex, PrintStream out, boolean verbose) {
        List<ScriptError> errors = worker.getErrors();
        try {
            worker.invoke("ringo/shell", "printError", ex,
                    new ScriptableList(scope, errors), Boolean.valueOf(verbose));
        } catch (Exception x) {
            // fall back to RingoRunner.reportError()
            RingoRunner.reportError(ex, out, errors, verbose);
        }
    }

    private void runSilently() throws IOException {
        int lineno = 0;
        outer: while (true) {
            Context cx = engine.getContextFactory().enterContext(null);
            cx.setErrorReporter(new ToolErrorReporter(false, System.err));
            String source = "";
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String line = reader.readLine();
                if (line == null) {
                    // reached EOF
                    break outer;
                }
                source = source + line + "\n";
                lineno++;
                if (cx.stringIsCompilableUnit(source))
                    break;
            }
            try {
                Resource res = new StringResource("<stdin>", source, lineno);
                ReloadableScript script = new ReloadableScript(res, engine);
                worker.evaluateScript(cx, script, scope);
                lineno++;
            } catch (Exception ex) {
                RingoRunner.reportError(ex, System.err, worker.getErrors(),
                        config.isVerbose());
            } finally {
                Context.exit();
            }
        }
        System.exit(0);
    }

    // preload ringo/shell in separate thread
    private void preloadShellModule() {
        Thread t = new Thread() {
            public void run() {
                Context cx = engine.getContextFactory().enterContext(null);
                try {
                    worker.loadModule(cx, "ringo/shell", null);
                } catch (Exception ignore) {
                    // ignore
                } finally {
                    Context.exit();
                }
            }
        };
        t.setPriority(Thread.MIN_PRIORITY);
        t.setDaemon(true);
        t.start();
    }

    class JSCompletor implements Completor {

        Pattern variables = Pattern.compile(
                "(^|\\s|[^\\w\\.'\"])([\\w\\.]+)$");
        Pattern keywords = Pattern.compile(
                "(^|\\s)([\\w]+)$");

        @SuppressWarnings("unchecked")
        public int complete(String s, int i, List list) {
            int start = i;
            try {
                Matcher match = keywords.matcher(s);
                if (match.find() && s.length() == i) {
                    String word = match.group(2);
                    for(String str: jsKeywords) {
                        if (str.startsWith(word)) {
                            list.add(str);
                        }
                    }
                }
                match = variables.matcher(s);
                if (match.find() && s.length() == i) {
                    String word = match.group(2);
                    Scriptable obj = scope;
                    String[] parts = word.split("\\.", -1);
                    for (int k = 0; k < parts.length - 1; k++) {
                        Object o = ScriptableObject.getProperty(obj, parts[k]);
                        if (o == null || o == ScriptableObject.NOT_FOUND) {
                            return start;
                        }
                        obj = ScriptRuntime.toObject(scope, o);
                    }
                    String lastpart = parts[parts.length - 1];
                    // set return value to beginning of word we're replacing
                    start = i - lastpart.length();
                    while (obj != null) {
                        // System.err.println(word + " -- " + obj);
                        Object[] ids = obj.getIds();
                        collectIds(ids, obj, word, lastpart, list);
                        if (list.size() <= 3 && obj instanceof ScriptableObject) {
                            ids = ((ScriptableObject) obj).getAllIds();
                            collectIds(ids, obj, word, lastpart, list);
                        }
                        if (word.endsWith(".") && obj instanceof ModuleScope) {
                            // don't walk scope prototype chain if nothing to compare yet -
                            // the list is just too long.
                            break;
                        }
                        obj = obj.getPrototype();
                    }
                }
            } catch (Exception ignore) {
                // ignore.printStackTrace();
            }
            Collections.sort(list);
            return start;
        }

        @SuppressWarnings("unchecked")
        private void collectIds(Object[] ids, Scriptable obj, String word, String lastpart, List list) {
            for(Object id: ids) {
                if (!(id instanceof String)) {
                    continue;
                }
                String str = (String) id;
                if (str.startsWith(lastpart) || word.endsWith(".")) {
                    if (ScriptableObject.getProperty(obj, str) instanceof Callable) {
                        list.add(str + "(");
                    } else {
                        list.add(str);
                    }
                }
            }
        }

    }

    static String[] jsKeywords =
        new String[] {
            "break",
            "case",
            "catch",
            "continue",
            "default",
            "delete",
            "do",
            "else",
            "finally",
            "for",
            "function",
            "if",
            "in",
            "instanceof",
            "new",
            "return",
            "switch",
            "this",
            "throw",
            "try",
            "typeof",
            "var",
            "void",
            "while",
            "with"
    };

}
TOP

Related Classes of org.ringojs.tools.RingoShell$JSCompletor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.