001package com.randomnoun.common.jessop.lang; 002 003/* (c) 2016 randomnoun. All Rights Reserved. This work is licensed under a 004 * BSD Simplified License. ( http://www.randomnoun.com/bsd-simplified.html ) 005 */ 006 007import javax.script.ScriptEngine; 008import javax.script.ScriptEngineFactory; 009import javax.script.ScriptEngineManager; 010 011import org.apache.log4j.Logger; 012 013import com.randomnoun.common.jessop.AbstractJessopScriptBuilder; 014import com.randomnoun.common.jessop.JessopScriptBuilder; 015 016public class JavascriptJessopScriptBuilder extends AbstractJessopScriptBuilder implements JessopScriptBuilder { 017 018 Logger logger = Logger.getLogger(JavascriptJessopScriptBuilder.class); 019 int outputLine = 1; // current line number in the target script; 020 int lastScriptletLine = 1; // the last line number of the last scriptlet (used for suppressEol) 021 022 public JavascriptJessopScriptBuilder() { 023 } 024 private void skipToLine(int line) { 025 while (outputLine < line) { print("\n"); } 026 } 027 private void print(String s) { 028 // logger.info("** PRINT " + s); 029 pw.print(s); 030 for (int i=0; i<s.length(); i++) { 031 if (s.charAt(i)=='\n') { outputLine++; } 032 } 033 } 034 private static String escapeJavascript(String string) { 035 StringBuilder sb = new StringBuilder(string.length()); 036 for (int i = 0; i<string.length(); i++) { 037 char ch = string.charAt(i); 038 if (ch=='\n') { 039 sb.append("\\n"); 040 } else if (ch=='\\' || ch=='"' || ch=='\'' || ch<32 || ch>126) { 041 String hex = Integer.toString(ch, 16); 042 sb.append("\\u" + "0000".substring(0, 4-hex.length()) + hex); 043 } else { 044 sb.append(ch); 045 } 046 } 047 return sb.toString(); 048 } 049 050 @Override 051 public void emitText(int line, String s) { 052 skipToLine(line); 053 s = suppressEol(s, declarations.isSuppressEol() && lastScriptletLine == line); 054 print("out.write(\"" + escapeJavascript(s) + "\");"); 055 lastScriptletLine = 0; // don't suppress eols on this line 056 } 057 @Override 058 public void emitExpression(int line, String s) { 059 skipToLine(line); 060 print("out.write('' + (" + s + "));"); // coerce to String 061 lastScriptletLine = 0; // don't suppress eols on this line 062 } 063 @Override 064 public void emitScriptlet(int line, String s) { 065 skipToLine(line); 066 print(s); 067 lastScriptletLine = line; 068 for (int i=0; i<s.length(); i++) { if (s.charAt(i)=='\n') { lastScriptletLine++; } } 069 } 070 @Override 071 public String getLanguage() { 072 return "javascript"; 073 } 074 @Override 075 public String getDefaultScriptEngineName() { 076 // the phobos jsr223 wrapper calls itself 'rhino-nonjdk', as well as 'rhino' 077 078 // if we have graalvm, then use that otherwise nashorn, otherwise rhino 079 ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal-js"); 080 if (engine!=null) { 081 return "graal-js"; 082 } else { 083 engine = new ScriptEngineManager().getEngineByName("nashorn"); 084 if (engine!=null) { 085 return "nashorn"; 086 } else { 087 return "rhino"; 088 } 089 } 090 } 091 092 093 // this method should probably take an engine name parameter which is the engine in effect 094 095 @Override 096 public String getDefaultBindingsConverterClassName() { 097 098 boolean isComSunRhino = false; // rhino engine is under the com.sun package 099 100 ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal-js"); 101 if (engine!=null) { 102 // com.oracle.truffle.js.scriptengine.GraalJSScriptEngine 103 return "com.randomnoun.common.jessop.engine.graaljs.GraalJsBindingsConverter"; 104 } 105 106 engine = new ScriptEngineManager().getEngineByName("nashorn"); 107 if (engine!=null) { 108 // jdk.nashorn.api.scripting.NashornScriptEngine 109 return null; // nashorn doesn't need a bindingsconverter 110 } 111 112 // let's see what class we get if we try to load the 'rhino' engine, then work from there 113 engine = new ScriptEngineManager().getEngineByName("rhino"); // nashorn in JDK9 114 if (engine!=null && engine.getClass().getName().equals("com.sun.script.javascript.RhinoScriptEngine")) { 115 // it's either oracle or openjdk 116 isComSunRhino = true; 117 } 118 String result = null; 119 if (!isComSunRhino) { 120 // maybe we've got com.sun.phobos:phobos-rhino 121 // or org.rhq:rhq-scripting-javascript 122 // or de.christophkraemer:rhino-script-engine 123 // or any of the other JSR223 wrappers for rhino in central 124 // at http://search.maven.org/#search%7Cga%7C1%7Cc%3A%22RhinoScriptEngine%22 125 try { 126 /*Class c =*/ Class.forName("org.mozilla.javascript.NativeObject"); 127 // this exists, so use the mozilla rhino binding converter 128 result = "com.randomnoun.common.jessop.engine.rhino.RhinoBindingsConverter"; 129 } catch (ClassNotFoundException cnfe) { } 130 } 131 132 // ok, it's probably oracle or openjdk at this stage 133 if (result == null) { 134 try { 135 /*Class c =*/ Class.forName("sun.org.mozilla.javascript.internal.NativeObject"); 136 // this exists, so use the oracle binding converter 137 result = "com.randomnoun.common.jessop.engine.rhinoOracle.RhinoOracleBindingsConverter"; 138 } catch (ClassNotFoundException cnfe2) { } 139 } 140 141 if (result == null) { 142 try { 143 /*Class c =*/ Class.forName("sun.org.mozilla.javascript.NativeObject"); 144 // this exists, so use the openjdk binding converter 145 result = "com.randomnoun.common.jessop.engine.rhinoOpenjdk.RhinoOpenjdkBindingsConverter"; 146 } catch (ClassNotFoundException cnfe3) { 147 // logger.warn("No known rhino implementation on classpath; setting JessopBindingsConverter to null"); 148 result = null; 149 } 150 } 151 152 return result; 153 } 154 155 @Override 156 public String getDefaultExceptionConverterClassName() { 157 ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal-js"); 158 if (engine!=null) { 159 return "com.randomnoun.common.jessop.engine.graaljs.GraalJsExceptionConverter"; 160 } 161 return null; // graal doesn't need a bindingsconverter 162 } 163 164 165 166 // going to use this for debugging only 167 public void testEngine() { 168 // this should match the engine in use, so let's see what 'rhino' gives us 169 ScriptEngine engine = new ScriptEngineManager().getEngineByName("rhino"); // nashorn in JDK9 170 if (engine!=null) { 171 ScriptEngineFactory factory = engine.getFactory(); 172 logger.info("default rhino ScriptEngine is " + engine.getClass().getName()); 173 logger.info("ENGINE=" + factory.getParameter(ScriptEngine.ENGINE)); 174 logger.info("ENGINE_VERSION=" + factory.getParameter(ScriptEngine.ENGINE_VERSION)); 175 logger.info("LANGUAGE=" + factory.getParameter(ScriptEngine.LANGUAGE_VERSION)); 176 logger.info("LANGUAGE_VERSION=" + factory.getParameter(ScriptEngine.LANGUAGE_VERSION)); 177 } else { 178 logger.warn("default 'rhino' ScriptEngine not found"); 179 } 180 } 181 182}