package act.app;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.Act;
import act.conf.AppConfig;
import act.metric.Metric;
import act.metric.Timer;
import act.util.DestroyableBase;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.osgl.$;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.S;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import static org.eclipse.jdt.internal.compiler.impl.CompilerOptions.*;
/**
* Compile App srccode code in memory. Only used when Act is running
* in DEV mode
*/
class AppCompiler extends DestroyableBase {
Map<String, Boolean> packagesCache = C.newMap();
private DevModeClassLoader classLoader;
private App app;
private AppConfig conf;
private CompilerOptions compilerOptions;
private Metric metric;
AppCompiler(DevModeClassLoader classLoader) {
this.classLoader = classLoader;
this.app = classLoader.app();
this.conf = app.config();
this.metric = Act.metricPlugin().metric("act.classload.compile");
configureCompilerOptions();
}
@Override
protected void releaseResources() {
packagesCache.clear();
super.releaseResources();
}
private void configureCompilerOptions() {
Map<String, String> map = C.newMap();
opt(map, OPTION_ReportMissingSerialVersion, IGNORE);
opt(map, OPTION_LineNumberAttribute, GENERATE);
opt(map, OPTION_SourceFileAttribute, GENERATE);
opt(map, OPTION_LocalVariableAttribute, GENERATE);
opt(map, OPTION_PreserveUnusedLocal, PRESERVE);
opt(map, OPTION_ReportDeprecation, IGNORE);
opt(map, OPTION_ReportUnusedImport, IGNORE);
opt(map, OPTION_Encoding, "UTF-8");
opt(map, OPTION_Process_Annotations, ENABLED);
opt(map, OPTION_Source, conf.sourceVersion());
opt(map, OPTION_TargetPlatform, conf.targetVersion());
opt(map, OPTION_Compliance, conf.sourceVersion());
compilerOptions = new CompilerOptions(map);
}
private void opt(Map map, String key, String val) {
map.put(key, val);
}
public void compile(Collection<Source> sources) {
Timer timer = metric.startTimer("act:classload:compile:_all");
int len = sources.size();
ICompilationUnit[] compilationUnits = new ICompilationUnit[len];
int i = 0;
for (Source source: sources) {
compilationUnits[i++] = source.compilationUnit();
}
IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitOnFirstError();
IProblemFactory problemFactory = new DefaultProblemFactory(Locale.ENGLISH);
org.eclipse.jdt.internal.compiler.Compiler jdtCompiler = new Compiler(
nameEnv, policy, compilerOptions, requestor, problemFactory) {
@Override
protected void handleInternalException(Throwable e, CompilationUnitDeclaration ud, CompilationResult result) {
}
};
jdtCompiler.compile(compilationUnits);
timer.stop();
}
public void compile(String className) {
Timer timer = metric.startTimer("act:classload:compile:" + className);
ICompilationUnit[] compilationUnits = new ICompilationUnit[1];
compilationUnits[0] = classLoader.source(className).compilationUnit();
IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitOnFirstError();
IProblemFactory problemFactory = new DefaultProblemFactory(Locale.ENGLISH);
org.eclipse.jdt.internal.compiler.Compiler jdtCompiler = new Compiler(
nameEnv, policy, compilerOptions, requestor, problemFactory) {
@Override
protected void handleInternalException(Throwable e, CompilationUnitDeclaration ud, CompilationResult result) {
}
};
jdtCompiler.compile(compilationUnits);
timer.stop();
}
private INameEnvironment nameEnv = new INameEnvironment() {
@Override
public NameEnvironmentAnswer findType(char[][] chars) {
final S.Buffer result = S.buffer();
for (int i = 0; i < chars.length; i++) {
if (i != 0) {
result.append('.');
}
result.append(chars[i]);
}
return findType(result.toString());
}
@Override
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
final S.Buffer result = S.buffer();
for (int i = 0; i < packageName.length; i++) {
result.append(packageName[i]);
result.append('.');
}
result.append(typeName);
return findType(result.toString());
}
@Override
public boolean isPackage(char[][] parentPackageName, char[] packageName) {
S.Buffer sb = S.buffer();
if (parentPackageName != null) {
for (char[] p : parentPackageName) {
sb.append(new String(p));
sb.append(".");
}
}
sb.append(new String(packageName));
String name = sb.toString();
if (packagesCache.containsKey(name)) {
return packagesCache.get(name);
}
if (classLoader.source(name) != null) {
packagesCache.put(name, false);
return false;
}
// Check if there is a .java or .class for this resource
if (classLoader.bytecode(name) != null) {
packagesCache.put(name, false);
return false;
}
packagesCache.put(name, true);
return true;
}
@Override
public void cleanup() {
}
private NameEnvironmentAnswer findType(String type) {
try {
byte[] bytes;
Source source;
if (Act.isDev()) {
source = classLoader.source(type);
if (null != source) {
return new NameEnvironmentAnswer(source.compilationUnit(), null);
}
}
bytes = classLoader.enhancedBytecode(type);
if (bytes != null) {
ClassFileReader classFileReader = new ClassFileReader(bytes, type.toCharArray(), true);
return new NameEnvironmentAnswer(classFileReader, null);
} else {
if (type.startsWith("org.osgl") || type.startsWith("java.") || type.startsWith("javax.")) {
return null;
}
}
if (Act.isDev()) {
return null;
}
source = classLoader.source(type);
if (null == source) {
return null;
} else {
return new NameEnvironmentAnswer(source.compilationUnit(), null);
}
} catch (ClassFormatException e) {
throw E.unexpected(e);
}
}
};
private ICompilerRequestor requestor = new ICompilerRequestor() {
@Override
public void acceptResult(CompilationResult result) {
// If error
if (result.hasErrors()) {
for (IProblem problem : result.getErrors()) {
char[][] caa = result.packageName;
if (null == caa) {
caa = result.compilationUnit.getPackageName();
}
S.Buffer sb = S.buffer();
if (null != caa) {
for (char[] ca : caa) {
sb.append(ca).append(".");
}
}
String className = sb.append(new String(problem.getOriginatingFileName())).toString();
className = className.substring(0, className.length() - 5);
String message = problem.getMessage();
if (problem.getID() == IProblem.CannotImportPackage) {
// Non sense !
message = problem.getArguments()[0] + " cannot be resolved";
}
Source src = classLoader.source(className);
if (null != src) {
throw new CompilationException(src.file(), message, problem.getSourceLineNumber(), problem.getSourceStart(), problem.getSourceEnd());
} else {
throw new CompilationException(problem.getMessage());
}
}
}
// Something has been compiled
ClassFile[] clazzFiles = result.getClassFiles();
for (int i = 0; i < clazzFiles.length; i++) {
final ClassFile clazzFile = clazzFiles[i];
final char[][] compoundName = clazzFile.getCompoundName();
final StringBuffer clazzName = new StringBuffer();
for (int j = 0; j < compoundName.length; j++) {
if (j != 0) {
clazzName.append('.');
}
clazzName.append(compoundName[j]);
}
String name = clazzName.toString();
String name0 = name;
if (name.contains("$")) {
name0 = S.beforeFirst(name, "$");
}
Source source = classLoader.source(name0);
if (null == source) {
$.nil();
source = classLoader.source(name0);
}
if (name != name0) {
String innerName = S.afterFirst(name, "$");
source.compiled(innerName, clazzFile.getBytes());
} else {
source.compiled(clazzFile.getBytes());
}
}
}
};
}