View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jci.compilers;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.List;
28  
29  import org.apache.commons.jci.problems.CompilationProblem;
30  import org.apache.commons.jci.readers.ResourceReader;
31  import org.apache.commons.jci.stores.ResourceStore;
32  import org.apache.commons.jci.utils.ConversionUtils;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.mozilla.javascript.CompilerEnvirons;
36  import org.mozilla.javascript.Context;
37  import org.mozilla.javascript.ErrorReporter;
38  import org.mozilla.javascript.EvaluatorException;
39  import org.mozilla.javascript.GeneratedClassLoader;
40  import org.mozilla.javascript.ImporterTopLevel;
41  import org.mozilla.javascript.JavaScriptException;
42  import org.mozilla.javascript.NativeArray;
43  import org.mozilla.javascript.Scriptable;
44  import org.mozilla.javascript.ScriptableObject;
45  import org.mozilla.javascript.optimizer.ClassCompiler;
46  
47  /**
48   * @author tcurdt
49   */
50  public final class RhinoJavaCompiler extends AbstractJavaCompiler {
51  
52      private final Log log = LogFactory.getLog(RhinoJavaCompiler.class);
53  
54      private final JavaCompilerSettings defaultSettings;
55      
56      
57      public RhinoJavaCompiler() {
58          defaultSettings = new RhinoJavaCompilerSettings();
59      }
60      
61      /**
62       * based on code from dev.helma.org
63       * http://dev.helma.org/source/file/helma/branches/rhinoloader/src/org/helma/javascript/RhinoLoader.java/?revision=95
64       */
65      private final class RhinoCompilingClassLoader extends ClassLoader {
66  
67          private final ScriptableObject scope;
68          private final ResourceReader reader;
69          private final ResourceStore store;
70  
71          private final Collection<CompilationProblem> problems = new ArrayList<CompilationProblem>();
72          
73          private final class ProblemCollector implements ErrorReporter {
74  
75              public void error(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
76  
77                  final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, true); 
78  
79                  if (problemHandler != null) {
80                      problemHandler.handle(problem);
81                  }
82  
83                  problems.add(problem); 
84              }
85  
86              public void warning(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
87  
88                  final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, false); 
89  
90                  if (problemHandler != null) {
91                      problemHandler.handle(problem);
92                  }
93  
94                  problems.add(problem); 
95              }
96  
97              public EvaluatorException runtimeError(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
98                  return new EvaluatorException(pMessage, pFileName, pLine, pScript, pColumn);
99              }
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 }