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  
28  import org.apache.commons.jci.problems.CompilationProblem;
29  import org.apache.commons.jci.readers.ResourceReader;
30  import org.apache.commons.jci.stores.ResourceStore;
31  import org.apache.commons.jci.utils.ConversionUtils;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.mozilla.javascript.CompilerEnvirons;
35  import org.mozilla.javascript.Context;
36  import org.mozilla.javascript.ErrorReporter;
37  import org.mozilla.javascript.EvaluatorException;
38  import org.mozilla.javascript.GeneratedClassLoader;
39  import org.mozilla.javascript.ImporterTopLevel;
40  import org.mozilla.javascript.JavaScriptException;
41  import org.mozilla.javascript.NativeArray;
42  import org.mozilla.javascript.Scriptable;
43  import org.mozilla.javascript.ScriptableObject;
44  import org.mozilla.javascript.optimizer.ClassCompiler;
45  
46  /**
47   * @author tcurdt
48   */
49  public final class RhinoJavaCompiler extends AbstractJavaCompiler {
50  
51      private final Log log = LogFactory.getLog(RhinoJavaCompiler.class);
52  
53      private final JavaCompilerSettings defaultSettings;
54      
55      
56      public RhinoJavaCompiler() {
57          defaultSettings = new RhinoJavaCompilerSettings();
58      }
59      
60      /**
61       * based on code from dev.helma.org
62       * http://dev.helma.org/source/file/helma/branches/rhinoloader/src/org/helma/javascript/RhinoLoader.java/?revision=95
63       */
64      private final class RhinoCompilingClassLoader extends ClassLoader {
65  
66          private final ScriptableObject scope;
67          private final ResourceReader reader;
68          private final ResourceStore store;
69  
70          private final Collection problems = new ArrayList();
71          
72          private final class ProblemCollector implements ErrorReporter {
73  
74              public void error(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
75  
76                  final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, true); 
77  
78                  if (problemHandler != null) {
79                      problemHandler.handle(problem);
80                  }
81  
82                  problems.add(problem); 
83              }
84  
85              public void warning(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
86  
87                  final CompilationProblem problem = new RhinoCompilationProblem(pMessage, pFileName, pLine, pScript, pColumn, false); 
88  
89                  if (problemHandler != null) {
90                      problemHandler.handle(problem);
91                  }
92  
93                  problems.add(problem); 
94              }
95  
96              public EvaluatorException runtimeError(String pMessage, String pFileName, int pLine, String pScript, int pColumn) {
97                  return new EvaluatorException(pMessage, pFileName, pLine, pScript, pColumn);
98              }
99          }
100 
101         public RhinoCompilingClassLoader( final ResourceReader pReader, final ResourceStore pStore, final ClassLoader pClassLoader) {
102             super(pClassLoader);
103 
104             reader = pReader;
105             store = pStore;
106 
107             final Context context = Context.enter();
108             scope = new ImporterTopLevel(context);
109             Context.exit();
110         }
111 
112         public Collection getProblems() {
113             return problems;
114         }
115 
116         protected Class findClass( final String pName ) throws ClassNotFoundException {
117             final Context context = Context.enter();
118             context.setErrorReporter(new ProblemCollector());
119 
120             try {
121                 return compileClass(context, pName);
122             } catch( EvaluatorException e ) {
123                 throw new ClassNotFoundException(e.getMessage(), e);
124             } catch (IOException e) {
125                 throw new ClassNotFoundException(e.getMessage(), e);
126             } finally {
127                 Context.exit();
128             }
129         }
130 
131 
132         private Class compileClass( final Context pContext, final String pClassName) throws IOException, ClassNotFoundException {
133 
134             Class superclass = null;
135 
136             final String pSourceName = pClassName.replace('.', '/') + ".js";
137 
138             final Scriptable target = evaluate(pContext, pSourceName);
139 
140             final Object baseClassName = ScriptableObject.getProperty(target, "__extends__");
141 
142             if (baseClassName instanceof String) {
143                 superclass = Class.forName((String) baseClassName);
144             }
145 
146             final ArrayList interfaceClasses = new ArrayList();
147 
148             final Object interfaceNames = ScriptableObject.getProperty(target, "__implements__");
149 
150             if (interfaceNames instanceof NativeArray) {
151 
152                 final NativeArray interfaceNameArray = (NativeArray) interfaceNames;
153 
154                 for (int i=0; i<interfaceNameArray.getLength(); i++) {
155 
156                     final Object obj = interfaceNameArray.get(i, interfaceNameArray);
157 
158                     if (obj instanceof String) {
159                         interfaceClasses.add(Class.forName((String) obj));
160                     }
161                 }
162 
163             } else if (interfaceNames instanceof String) {
164 
165                 interfaceClasses.add(Class.forName((String) interfaceNames));
166 
167             }
168 
169             final Class[] interfaces;
170 
171             if (!interfaceClasses.isEmpty()) {
172                 interfaces = new Class[interfaceClasses.size()];
173                 interfaceClasses.toArray(interfaces);
174             } else {
175                 // FIXME: hm ...really no empty array good enough?
176                 interfaces = null;
177             }
178 
179             return compileClass(pContext, pSourceName, pClassName, superclass, interfaces);
180 
181         }
182 
183 
184         private Class compileClass( final Context pContext, final String pSourceName, final String pClassName, final Class pSuperClass, final Class[] pInterfaces) throws IOException {
185 
186             final CompilerEnvirons environments = new CompilerEnvirons();
187             environments.initFromContext(pContext);
188             final ClassCompiler compiler = new ClassCompiler(environments);
189 
190             if (pSuperClass != null) {
191                 compiler.setTargetExtends(pSuperClass);
192             }
193 
194             if (pInterfaces != null) {
195                 compiler.setTargetImplements(pInterfaces);
196             }
197 
198             final byte[] sourceBytes = reader.getBytes(pSourceName);
199 
200             final Object[] classes = compiler.compileToClassFiles(new String(sourceBytes), getName(pSourceName), 1, pClassName);
201 
202             final GeneratedClassLoader loader = pContext.createClassLoader(pContext.getApplicationClassLoader());
203 
204             Class clazz = null;
205 
206             for (int i = 0; i < classes.length; i += 2) {
207 
208                 final String clazzName = (String) classes[i];
209                 final byte[] clazzBytes = (byte[]) classes[i+1];
210 
211                 store.write(clazzName.replace('.', '/') + ".class", clazzBytes);
212 
213                 Class c = loader.defineClass(clazzName, clazzBytes);
214                 loader.linkClass(c);
215 
216                 if (i == 0) {
217                     clazz = c;
218                 }
219 
220             }
221 
222             return clazz;
223         }
224 
225         private String getName(String s) {
226             final int i = s.lastIndexOf('/');
227             if (i < 0) {
228                 return s;
229             }
230 
231             return s.substring(i + 1);
232         }
233 
234         private Scriptable evaluate( final Context pContext, final String pSourceName) throws JavaScriptException, IOException {
235 
236             if (!reader.isAvailable(pSourceName)) {
237                 throw new FileNotFoundException("File " + pSourceName + " not found");
238             }
239 
240             final Scriptable target = pContext.newObject(scope);
241 
242             final byte[] sourceBytes = reader.getBytes(pSourceName);
243 
244             final Reader reader = new InputStreamReader(new ByteArrayInputStream(sourceBytes));
245 
246             pContext.evaluateReader(target, reader, getName(pSourceName), 1, null);
247 
248             return target;
249         }
250 
251     }
252     
253     
254     public CompilationResult compile( final String[] pResourcePaths, final ResourceReader pReader, final ResourceStore pStore, final ClassLoader pClassLoader, final JavaCompilerSettings pSettings ) {
255 
256         final RhinoCompilingClassLoader cl = new RhinoCompilingClassLoader(pReader, pStore, pClassLoader);
257 
258         for (int i = 0; i < pResourcePaths.length; i++) {
259             log.debug("compiling " + pResourcePaths[i]);
260             
261             final String clazzName = ConversionUtils.convertResourceToClassName(pResourcePaths[i]);
262             try {
263                 cl.loadClass(clazzName);
264             } catch (ClassNotFoundException e) {
265             }
266         }
267 
268         final Collection problems = cl.getProblems();
269         final CompilationProblem[] result = new CompilationProblem[problems.size()];
270         problems.toArray(result);
271         return new CompilationResult(result);
272     }
273 
274 
275     public JavaCompilerSettings createDefaultSettings() {
276         return defaultSettings;
277     }
278 
279 }