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 }