001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.jci.compilers; 019 020 import java.io.ByteArrayInputStream; 021 import java.io.FileNotFoundException; 022 import java.io.IOException; 023 import java.io.InputStreamReader; 024 import java.io.Reader; 025 import java.util.ArrayList; 026 import java.util.Collection; 027 import java.util.List; 028 029 import org.apache.commons.jci.problems.CompilationProblem; 030 import org.apache.commons.jci.readers.ResourceReader; 031 import org.apache.commons.jci.stores.ResourceStore; 032 import org.apache.commons.jci.utils.ConversionUtils; 033 import org.apache.commons.logging.Log; 034 import org.apache.commons.logging.LogFactory; 035 import org.mozilla.javascript.CompilerEnvirons; 036 import org.mozilla.javascript.Context; 037 import org.mozilla.javascript.ErrorReporter; 038 import org.mozilla.javascript.EvaluatorException; 039 import org.mozilla.javascript.GeneratedClassLoader; 040 import org.mozilla.javascript.ImporterTopLevel; 041 import org.mozilla.javascript.JavaScriptException; 042 import org.mozilla.javascript.NativeArray; 043 import org.mozilla.javascript.Scriptable; 044 import org.mozilla.javascript.ScriptableObject; 045 import org.mozilla.javascript.optimizer.ClassCompiler; 046 047 /** 048 * @author tcurdt 049 */ 050 public final class RhinoJavaCompiler extends AbstractJavaCompiler { 051 052 private final Log log = LogFactory.getLog(RhinoJavaCompiler.class); 053 054 private final JavaCompilerSettings defaultSettings; 055 056 057 public RhinoJavaCompiler() { 058 defaultSettings = new RhinoJavaCompilerSettings(); 059 } 060 061 /** 062 * based on code from dev.helma.org 063 * http://dev.helma.org/source/file/helma/branches/rhinoloader/src/org/helma/javascript/RhinoLoader.java/?revision=95 064 */ 065 private final class RhinoCompilingClassLoader extends ClassLoader { 066 067 private final ScriptableObject scope; 068 private final ResourceReader reader; 069 private final ResourceStore store; 070 071 private final Collection<CompilationProblem> problems = new ArrayList<CompilationProblem>(); 072 073 private final class ProblemCollector implements ErrorReporter { 074 075 public void error(String pMessage, String pFileName, int pLine, String pScript, int pColumn) { 076 077 final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, true); 078 079 if (problemHandler != null) { 080 problemHandler.handle(problem); 081 } 082 083 problems.add(problem); 084 } 085 086 public void warning(String pMessage, String pFileName, int pLine, String pScript, int pColumn) { 087 088 final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, false); 089 090 if (problemHandler != null) { 091 problemHandler.handle(problem); 092 } 093 094 problems.add(problem); 095 } 096 097 public EvaluatorException runtimeError(String pMessage, String pFileName, int pLine, String pScript, int pColumn) { 098 return new EvaluatorException(pMessage, pFileName, pLine, pScript, pColumn); 099 } 100 } 101 102 public RhinoCompilingClassLoader( final ResourceReader pReader, final ResourceStore pStore, final ClassLoader pClassLoader) { 103 super(pClassLoader); 104 105 reader = pReader; 106 store = pStore; 107 108 final Context context = Context.enter(); 109 scope = new ImporterTopLevel(context); 110 Context.exit(); 111 } 112 113 public Collection<CompilationProblem> getProblems() { 114 return problems; 115 } 116 117 @Override 118 protected Class<?> findClass( final String pName ) throws ClassNotFoundException { 119 final Context context = Context.enter(); 120 context.setErrorReporter(new ProblemCollector()); 121 122 try { 123 return compileClass(context, pName); 124 } catch( EvaluatorException e ) { 125 throw new ClassNotFoundException(e.getMessage(), e); 126 } catch (IOException e) { 127 throw new ClassNotFoundException(e.getMessage(), e); 128 } finally { 129 Context.exit(); 130 } 131 } 132 133 134 private Class<?> compileClass( final Context pContext, final String pClassName) throws IOException, ClassNotFoundException { 135 136 Class<?> superclass = null; 137 138 final String pSourceName = pClassName.replace('.', '/') + ".js"; 139 140 final Scriptable target = evaluate(pContext, pSourceName); 141 142 final Object baseClassName = ScriptableObject.getProperty(target, "__extends__"); 143 144 if (baseClassName instanceof String) { 145 superclass = Class.forName((String) baseClassName); 146 } 147 148 final List<Class<?>> interfaceClasses = new ArrayList<Class<?>>(); 149 150 final Object interfaceNames = ScriptableObject.getProperty(target, "__implements__"); 151 152 if (interfaceNames instanceof NativeArray) { 153 154 final NativeArray interfaceNameArray = (NativeArray) interfaceNames; 155 156 for (int i=0; i<interfaceNameArray.getLength(); i++) { 157 158 final Object obj = interfaceNameArray.get(i, interfaceNameArray); 159 160 if (obj instanceof String) { 161 interfaceClasses.add(Class.forName((String) obj)); 162 } 163 } 164 165 } else if (interfaceNames instanceof String) { 166 167 interfaceClasses.add(Class.forName((String) interfaceNames)); 168 169 } 170 171 final Class<?>[] interfaces; 172 173 if (!interfaceClasses.isEmpty()) { 174 interfaces = new Class[interfaceClasses.size()]; 175 interfaceClasses.toArray(interfaces); 176 } else { 177 // FIXME: hm ...really no empty array good enough? 178 interfaces = null; 179 } 180 181 return compileClass(pContext, pSourceName, pClassName, superclass, interfaces); 182 183 } 184 185 186 private Class<?> compileClass( final Context pContext, final String pSourceName, final String pClassName, final Class<?> pSuperClass, final Class<?>[] pInterfaces) { 187 188 final CompilerEnvirons environments = new CompilerEnvirons(); 189 environments.initFromContext(pContext); 190 final ClassCompiler compiler = new ClassCompiler(environments); 191 192 if (pSuperClass != null) { 193 compiler.setTargetExtends(pSuperClass); 194 } 195 196 if (pInterfaces != null) { 197 compiler.setTargetImplements(pInterfaces); 198 } 199 200 final byte[] sourceBytes = reader.getBytes(pSourceName); 201 202 final Object[] classes = compiler.compileToClassFiles(new String(sourceBytes), getName(pSourceName), 1, pClassName); 203 204 final GeneratedClassLoader loader = pContext.createClassLoader(pContext.getApplicationClassLoader()); 205 206 Class<?> clazz = null; 207 208 for (int i = 0; i < classes.length; i += 2) { 209 210 final String clazzName = (String) classes[i]; 211 final byte[] clazzBytes = (byte[]) classes[i+1]; 212 213 store.write(clazzName.replace('.', '/') + ".class", clazzBytes); 214 215 Class<?> c = loader.defineClass(clazzName, clazzBytes); 216 loader.linkClass(c); 217 218 if (i == 0) { 219 clazz = c; 220 } 221 222 } 223 224 return clazz; 225 } 226 227 private String getName(String s) { 228 final int i = s.lastIndexOf('/'); 229 if (i < 0) { 230 return s; 231 } 232 233 return s.substring(i + 1); 234 } 235 236 private Scriptable evaluate( final Context pContext, final String pSourceName) throws JavaScriptException, IOException { 237 238 if (!reader.isAvailable(pSourceName)) { 239 throw new FileNotFoundException("File " + pSourceName + " not found"); 240 } 241 242 final Scriptable target = pContext.newObject(scope); 243 244 final byte[] sourceBytes = reader.getBytes(pSourceName); 245 246 final Reader reader = new InputStreamReader(new ByteArrayInputStream(sourceBytes)); 247 248 pContext.evaluateReader(target, reader, getName(pSourceName), 1, null); 249 250 return target; 251 } 252 253 } 254 255 256 public CompilationResult compile( final String[] pResourcePaths, final ResourceReader pReader, final ResourceStore pStore, final ClassLoader pClassLoader, final JavaCompilerSettings pSettings ) { 257 258 final RhinoCompilingClassLoader cl = new RhinoCompilingClassLoader(pReader, pStore, pClassLoader); 259 260 for (int i = 0; i < pResourcePaths.length; i++) { 261 log.debug("compiling " + pResourcePaths[i]); 262 263 final String clazzName = ConversionUtils.convertResourceToClassName(pResourcePaths[i]); 264 try { 265 cl.loadClass(clazzName); 266 } catch (ClassNotFoundException e) { 267 } 268 } 269 270 final Collection<CompilationProblem> problems = cl.getProblems(); 271 final CompilationProblem[] result = new CompilationProblem[problems.size()]; 272 problems.toArray(result); 273 return new CompilationResult(result); 274 } 275 276 277 public JavaCompilerSettings createDefaultSettings() { 278 return defaultSettings; 279 } 280 281 }