| 1 | |
package org.webslinger.bsf.janino; |
| 2 | |
|
| 3 | |
import java.io.IOException; |
| 4 | |
import java.io.StringReader; |
| 5 | |
import java.io.StringWriter; |
| 6 | |
import java.util.ArrayList; |
| 7 | |
import java.util.HashMap; |
| 8 | |
import java.util.Iterator; |
| 9 | |
import java.util.List; |
| 10 | |
|
| 11 | |
import org.apache.bsf.BSFDeclaredBean; |
| 12 | |
import org.apache.bsf.BSFException; |
| 13 | |
import org.apache.bsf.util.BSFFunctions; |
| 14 | |
|
| 15 | |
import org.codehaus.janino.ByteArrayClassLoader; |
| 16 | |
import org.codehaus.janino.ClassLoaderIClassLoader; |
| 17 | |
import org.codehaus.janino.CompileException; |
| 18 | |
import org.codehaus.janino.DebuggingInformation; |
| 19 | |
import org.codehaus.janino.Java; |
| 20 | |
import org.codehaus.janino.Location; |
| 21 | |
import org.codehaus.janino.Mod; |
| 22 | |
import org.codehaus.janino.Parser; |
| 23 | |
import org.codehaus.janino.Scanner; |
| 24 | |
import org.codehaus.janino.UnitCompiler; |
| 25 | |
import org.codehaus.janino.UnparseVisitor; |
| 26 | |
import org.codehaus.janino.util.ClassFile; |
| 27 | |
import org.codehaus.janino.util.enumerator.EnumeratorSet; |
| 28 | |
|
| 29 | |
import org.webslinger.bsf.ApplyKey; |
| 30 | |
import org.webslinger.bsf.Compiler; |
| 31 | |
import org.webslinger.bsf.EvalKey; |
| 32 | |
import org.webslinger.bsf.ExecKey; |
| 33 | |
import org.webslinger.bsf.LanguageEngine; |
| 34 | |
import org.webslinger.bsf.Key; |
| 35 | |
import org.webslinger.util.GeneratedResult; |
| 36 | |
|
| 37 | 780 | public abstract class AbstractJaninoCompiler<V extends Object, C extends Class<V>> extends Compiler<V, C> { |
| 38 | |
protected interface Init { |
| 39 | |
void init(LanguageEngine engine, Object[] beans); |
| 40 | |
} |
| 41 | |
|
| 42 | |
protected String removeStart(String text, StringBuilder imports, StringBuilder staticDefs) throws IOException, Scanner.LocatedException { |
| 43 | 70 | Scanner textScanner = new Scanner(null, new StringReader(text)); |
| 44 | 70 | textScanner.setSwallow(false); |
| 45 | 70 | StringWriter importWriter = new StringWriter(); |
| 46 | 70 | StringWriter staticWriter = new StringWriter(); |
| 47 | 70 | StringWriter writer = importWriter; |
| 48 | 70 | Parser textParser = new Parser(textScanner); |
| 49 | |
do { |
| 50 | 1190 | Scanner.Token token = textScanner.peek(); |
| 51 | 1190 | if (token.isComment()) { |
| 52 | 0 | textScanner.read(); |
| 53 | 1190 | } else if (token.isWhitespace()) { |
| 54 | 560 | textScanner.read(); |
| 55 | 630 | } else if (writer == importWriter && token.isKeyword("import")) { |
| 56 | 538 | textScanner.setSwallow(true); |
| 57 | 538 | textParser.parseImportDeclaration(); |
| 58 | 538 | textScanner.setSwallow(false); |
| 59 | 92 | } else if (token.isKeyword("static")) { |
| 60 | 22 | writer = staticWriter; |
| 61 | 22 | textScanner.read(); |
| 62 | 22 | token = textScanner.peek(); |
| 63 | 22 | textScanner.setSwallow(true); |
| 64 | 22 | short mod = (short) (Mod.STATIC | textParser.parseModifiersOpt()); |
| 65 | 22 | if (textParser.peekOperator("{")) { |
| 66 | 0 | textScanner.read(); |
| 67 | 0 | textParser.parseBlock(); |
| 68 | 22 | } else if (textParser.peekKeyword("class")) { |
| 69 | 12 | textScanner.read(); |
| 70 | 12 | textParser.parseClassDeclarationRest( |
| 71 | |
null, |
| 72 | |
mod, |
| 73 | |
Parser.ClassDeclarationContext.TYPE_DECLARATION |
| 74 | |
); |
| 75 | 10 | } else if (textParser.peekKeyword("interface")) { |
| 76 | 0 | textScanner.read(); |
| 77 | 0 | textParser.parseInterfaceDeclarationRest( |
| 78 | |
null, |
| 79 | |
mod, |
| 80 | |
Parser.InterfaceDeclarationContext.NAMED_TYPE_DECLARATION |
| 81 | |
); |
| 82 | 10 | } else if (textParser.peekKeyword("void")) { |
| 83 | 0 | textScanner.read(); |
| 84 | 0 | Location location = textParser.location(); |
| 85 | 0 | String name = textParser.readIdentifier(); |
| 86 | 0 | textParser.parseMethodDeclarationRest( |
| 87 | |
null, |
| 88 | |
mod, |
| 89 | |
new Java.BasicType(location, Java.BasicType.VOID), |
| 90 | |
name |
| 91 | |
); |
| 92 | 0 | } else { |
| 93 | 10 | Java.Type type = textParser.parseType(); |
| 94 | 10 | Location location = textParser.location(); |
| 95 | 10 | String name = textParser.readIdentifier(); |
| 96 | 10 | if (textParser.peekOperator("(")) { |
| 97 | 5 | textParser.parseMethodDeclarationRest( |
| 98 | |
null, |
| 99 | |
mod, |
| 100 | |
type, |
| 101 | |
name |
| 102 | |
); |
| 103 | |
} else { |
| 104 | 5 | new Java.FieldDeclaration( |
| 105 | |
location, |
| 106 | |
null, |
| 107 | |
mod, |
| 108 | |
type, |
| 109 | |
textParser.parseFieldDeclarationRest(name) |
| 110 | |
); |
| 111 | 5 | textParser.readOperator(";"); |
| 112 | |
} |
| 113 | |
} |
| 114 | 22 | textScanner.setSwallow(false); |
| 115 | |
} else { |
| 116 | |
break; |
| 117 | |
} |
| 118 | 1120 | writer.write(token.toString()); |
| 119 | 8960 | while (token.getNextToken() != null) { |
| 120 | 8960 | token = token.getNextToken(); |
| 121 | 8960 | if (token.getNextToken() == null) break; |
| 122 | 7840 | writer.write(token.toString()); |
| 123 | |
} |
| 124 | 1120 | } while (true); |
| 125 | 70 | imports.append(importWriter.getBuffer()); |
| 126 | 70 | staticDefs.append(staticWriter.getBuffer()); |
| 127 | 70 | textScanner.setSwallow(false); |
| 128 | 70 | StringBuilder newText = new StringBuilder(); |
| 129 | 48612 | while (!textScanner.peek().isEOF()) { |
| 130 | 48542 | Scanner.Token token = textScanner.read(); |
| 131 | |
|
| 132 | 48542 | String tokenText = token.toString(); |
| 133 | 48542 | newText.append(tokenText); |
| 134 | 48542 | } |
| 135 | 70 | return newText.toString(); |
| 136 | |
} |
| 137 | |
|
| 138 | 46 | protected final HashMap<Class, Class> implementsMap = new HashMap<Class, Class>(); |
| 139 | |
|
| 140 | |
public class JaninoCompilerContext extends CompilerContext { |
| 141 | |
protected String imports; |
| 142 | |
protected String staticDefs; |
| 143 | |
protected Object[] beanObjects; |
| 144 | 260 | protected JaninoCompilerContext(Key<C> key) throws BSFException { |
| 145 | 260 | super(key); |
| 146 | 260 | beanObjects = new Object[beans.length]; |
| 147 | 260 | for (int i = 0; i < beans.length; i++) { |
| 148 | 0 | beanObjects[i] = beans[i].bean; |
| 149 | |
} |
| 150 | 260 | } |
| 151 | |
} |
| 152 | |
|
| 153 | |
public AbstractJaninoCompiler(LanguageEngine engine) { |
| 154 | 46 | super(engine); |
| 155 | 46 | } |
| 156 | |
|
| 157 | |
protected String filter(CompilerContext context, String text) throws BSFException { |
| 158 | 260 | return filter((JaninoCompilerContext) context, text); |
| 159 | |
} |
| 160 | |
|
| 161 | |
protected String filter(JaninoCompilerContext context, String text) throws BSFException { |
| 162 | 260 | StringBuilder imports = new StringBuilder(); |
| 163 | 260 | StringBuilder staticDefs = new StringBuilder(); |
| 164 | |
try { |
| 165 | 260 | return removeStart(text, imports, staticDefs); |
| 166 | 0 | } catch (IOException e) { |
| 167 | 0 | throw (BSFException) new BSFException(BSFException.REASON_IO_ERROR, e.getMessage(), e).initCause(e); |
| 168 | 0 | } catch (Scanner.LocatedException e) { |
| 169 | 0 | throw (BSFException) new BSFException(BSFException.REASON_IO_ERROR, e.getMessage(), e).initCause(e); |
| 170 | |
} finally { |
| 171 | 260 | context.imports = imports.toString(); |
| 172 | 260 | context.staticDefs = staticDefs.toString(); |
| 173 | |
} |
| 174 | |
} |
| 175 | |
|
| 176 | |
public void declareBean(BSFDeclaredBean bean) throws BSFException { |
| 177 | 967 | clear(); |
| 178 | 967 | } |
| 179 | |
|
| 180 | |
public void undeclareBean(BSFDeclaredBean bean) throws BSFException { |
| 181 | 0 | clear(); |
| 182 | 0 | } |
| 183 | |
|
| 184 | |
|
| 185 | |
protected C compile(CompilerContext context) throws Throwable { |
| 186 | 260 | return compile((JaninoCompilerContext) context); |
| 187 | |
} |
| 188 | |
|
| 189 | |
protected CompilerContext newContext(Key<C> key) throws BSFException { |
| 190 | 260 | return new JaninoCompilerContext(key); |
| 191 | |
} |
| 192 | |
|
| 193 | |
protected static void addImplementsToCode(StringBuilder code, List<String> implementsList) { |
| 194 | 330 | if (!implementsList.isEmpty()) { |
| 195 | 140 | Iterator<String> it = implementsList.iterator(); |
| 196 | 140 | code.append(" implements"); |
| 197 | 280 | while (it.hasNext()) { |
| 198 | 140 | String interfaceName = it.next(); |
| 199 | 140 | code.append(' ').append(interfaceName); |
| 200 | 140 | if (it.hasNext()) code.append(','); |
| 201 | 140 | } |
| 202 | |
} |
| 203 | 330 | } |
| 204 | |
|
| 205 | |
protected void addHelperImplements(JaninoCompilerContext context, List<String> implementsList) { |
| 206 | 70 | implementsList.add(Init.class.getName()); |
| 207 | 70 | } |
| 208 | |
|
| 209 | |
protected void compileHelperVariables(JaninoCompilerContext context, StringBuilder code, String className) { |
| 210 | 70 | } |
| 211 | |
|
| 212 | |
protected void compileHelperMethods(JaninoCompilerContext context, StringBuilder code, String className) { |
| 213 | 70 | code.append("public final void init(").append(LanguageEngine.class.getName()).append(" engine, Object[] beans) {"); |
| 214 | 70 | code.append(className).append(".bsf = engine.getBSFFunctions();"); |
| 215 | 70 | for (int i = 0; i < context.beans.length; i++) { |
| 216 | 0 | BSFDeclaredBean bean = context.beans[i]; |
| 217 | 0 | code.append(className).append('.').append(bean.name).append(" = (").append(bean.type.getName()).append(") beans[").append(i).append("];"); |
| 218 | |
} |
| 219 | 70 | code.append('}'); |
| 220 | 70 | } |
| 221 | |
|
| 222 | |
protected void compileHelper(JaninoCompilerContext context, StringBuilder code, String className) { |
| 223 | 70 | List<String> implementsList = new ArrayList<String>(); |
| 224 | 70 | addHelperImplements(context, implementsList); |
| 225 | 70 | code.append("public final class ").append(className).append("Init"); |
| 226 | 70 | addImplementsToCode(code, implementsList); |
| 227 | 70 | code.append(" {"); |
| 228 | 70 | compileHelperVariables(context, code, className); |
| 229 | 70 | compileHelperMethods(context, code, className); |
| 230 | 70 | code.append('}'); |
| 231 | 70 | } |
| 232 | |
|
| 233 | |
protected void addClassImplements(JaninoCompilerContext context, List<String> implementsList) { |
| 234 | 260 | Class keyImplements = implementsMap.get(context.key.getClass()); |
| 235 | 260 | if (keyImplements != null) implementsList.add(keyImplements.getName()); |
| 236 | 260 | } |
| 237 | |
|
| 238 | |
protected void compileClassVariables(JaninoCompilerContext context, StringBuilder code, String className) { |
| 239 | 70 | code.append("static ").append(BSFFunctions.class.getName()).append(" bsf;"); |
| 240 | 70 | for (BSFDeclaredBean bean: context.beans) { |
| 241 | 0 | code.append("static ").append(bean.type.getName()).append(' ').append(bean.name).append(';'); |
| 242 | |
} |
| 243 | 70 | } |
| 244 | |
|
| 245 | |
protected void compileClassMethods(JaninoCompilerContext context, StringBuilder code, String className) throws Throwable { |
| 246 | 260 | super.compile(context); |
| 247 | 260 | } |
| 248 | |
|
| 249 | |
protected void addExtends(JaninoCompilerContext context, StringBuilder code) { |
| 250 | 70 | } |
| 251 | |
|
| 252 | |
protected void compileConstructors(JaninoCompilerContext context, StringBuilder code, String className) { |
| 253 | 70 | } |
| 254 | |
|
| 255 | |
protected void compileClass(JaninoCompilerContext context, StringBuilder code, String className) throws Throwable { |
| 256 | 260 | List<String> implementsList = new ArrayList<String>(); |
| 257 | 260 | addClassImplements(context, implementsList); |
| 258 | 260 | code.append("public class ").append(className); |
| 259 | 260 | addExtends(context, code); |
| 260 | 260 | addImplementsToCode(code, implementsList); |
| 261 | 260 | code.append(" {"); |
| 262 | 260 | compileClassVariables(context, code, className); |
| 263 | 260 | compileConstructors(context, code, className); |
| 264 | 260 | code.append(context.staticDefs); |
| 265 | 260 | compileClassMethods(context, code, className); |
| 266 | 260 | code.append('}'); |
| 267 | 260 | } |
| 268 | |
|
| 269 | |
protected C compile(JaninoCompilerContext context) throws Throwable { |
| 270 | 260 | String fileName = engine.getVFSDelegate().absolutePath(context.key.getId()); |
| 271 | 260 | StringBuilder packageNameBuffer = new StringBuilder(); |
| 272 | 260 | StringBuilder classNameBuffer = new StringBuilder(); |
| 273 | 260 | nameEncoder.makePackageClassName(packageNameBuffer, classNameBuffer, fileName); |
| 274 | |
|
| 275 | 260 | ClassLoaderIClassLoader classLoaderIClassLoader = new ClassLoaderIClassLoader(context.key.getParentClassLoader(engine)); |
| 276 | |
|
| 277 | 260 | StringBuilder code = context.code; |
| 278 | 260 | if (packageNameBuffer.length() > 0) code.append("package ").append(packageNameBuffer).append(';'); |
| 279 | 260 | code.append(context.imports); |
| 280 | 260 | String className = classNameBuffer.toString(); |
| 281 | 260 | compileHelper(context, code, className); |
| 282 | 260 | compileClass(context, code, className); |
| 283 | 260 | Parser parser = new Parser(new Scanner(fileName, new StringReader(code.toString()))); |
| 284 | |
|
| 285 | 260 | if (engine.isDebugOn()) System.err.println(code); |
| 286 | 260 | Java.CompilationUnit unit = parser.parseCompilationUnit(); |
| 287 | |
|
| 288 | |
|
| 289 | |
|
| 290 | 260 | EnumeratorSet debugInfo = DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION; |
| 291 | |
|
| 292 | 260 | final StringBuilder errorMessage = new StringBuilder(); |
| 293 | 260 | UnitCompiler.ErrorHandler errorHandler = new UnitCompiler.ErrorHandler() { |
| 294 | |
public void handleError(String message, Location location) { |
| 295 | 0 | if (errorMessage.length() > 0) errorMessage.append('\n'); |
| 296 | 0 | if (location != null) errorMessage.append(location).append(": "); |
| 297 | 0 | errorMessage.append(message); |
| 298 | 0 | } |
| 299 | |
}; |
| 300 | |
|
| 301 | 260 | UnitCompiler uc = new UnitCompiler(unit, classLoaderIClassLoader); |
| 302 | 260 | uc.setCompileErrorHandler(errorHandler); |
| 303 | 260 | ClassFile[] classFiles = null; |
| 304 | 260 | Exception caughtException = null; |
| 305 | |
try { |
| 306 | 260 | classFiles = uc.compileUnit(debugInfo); |
| 307 | 0 | } catch (RuntimeException e) { |
| 308 | 0 | caughtException = e; |
| 309 | 0 | } catch (CompileException e) { |
| 310 | 0 | caughtException = e; |
| 311 | |
|
| 312 | |
|
| 313 | 260 | } |
| 314 | 260 | if (errorMessage.length() > 0) throw new CompileException(errorMessage.toString(), null); |
| 315 | 260 | if (caughtException != null) throw new CompileException("Error compiling", null, caughtException); |
| 316 | |
|
| 317 | |
|
| 318 | 260 | HashMap<String, byte[]> classes = new HashMap<String, byte[]>(); |
| 319 | 602 | for (ClassFile cf: classFiles) { |
| 320 | 342 | classes.put(cf.getThisClassName(), cf.toByteArray()); |
| 321 | |
} |
| 322 | |
|
| 323 | |
|
| 324 | 260 | ClassLoader classLoader = new ByteArrayClassLoader(classes, classLoaderIClassLoader.getClassLoader()); |
| 325 | 260 | if (packageNameBuffer.length() > 0) className = packageNameBuffer.append('.').append(className).toString(); |
| 326 | 260 | return (C) classLoader.loadClass(className); |
| 327 | |
} |
| 328 | |
|
| 329 | |
protected V newInstance(JaninoCompilerContext context, C clz) throws Throwable { |
| 330 | 70 | return clz.newInstance(); |
| 331 | |
} |
| 332 | |
|
| 333 | |
protected V init(CompilerContext context, C compiled) throws Throwable { |
| 334 | 260 | initHelper(context, compiled); |
| 335 | 260 | return newInstance((JaninoCompilerContext) context, compiled); |
| 336 | |
} |
| 337 | |
|
| 338 | |
protected void initHelper(CompilerContext context, C compiled) throws Throwable { |
| 339 | 70 | Init init = (Init) compiled.getClassLoader().loadClass(compiled.getName() + "Init").newInstance(); |
| 340 | 70 | init.init(engine, ((JaninoCompilerContext) context).beanObjects); |
| 341 | 70 | } |
| 342 | |
|
| 343 | |
public C compileKey(ApplyKey<C> key, CompilerContext context) throws BSFException { |
| 344 | 260 | return compileKey(key, (JaninoCompilerContext) context); |
| 345 | |
} |
| 346 | |
|
| 347 | |
public C compileKey(EvalKey<C> key, CompilerContext context) throws BSFException { |
| 348 | 0 | return compileKey(key, (JaninoCompilerContext) context); |
| 349 | |
} |
| 350 | |
|
| 351 | |
public C compileKey(ExecKey<C> key, CompilerContext context) throws BSFException { |
| 352 | 0 | return compileKey(key, (JaninoCompilerContext) context); |
| 353 | |
} |
| 354 | |
|
| 355 | |
protected abstract C compileKey(ApplyKey<C> key, JaninoCompilerContext context) throws BSFException; |
| 356 | |
protected abstract C compileKey(EvalKey<C> key, JaninoCompilerContext context) throws BSFException; |
| 357 | |
protected abstract C compileKey(ExecKey<C> key, JaninoCompilerContext context) throws BSFException; |
| 358 | |
} |