    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    }