package org.develnext.jphp.core.compiler.jvm; import org.develnext.jphp.core.tokenizer.token.expr.value.YieldExprToken; import php.runtime.Memory; import php.runtime.env.Context; import php.runtime.env.Environment; import php.runtime.reflection.*; import php.runtime.reflection.helper.ClosureEntity; import php.runtime.common.Messages; import php.runtime.common.Modifier; import php.runtime.common.AbstractCompiler; import org.develnext.jphp.core.compiler.jvm.statement.*; import php.runtime.exceptions.CompileException; import php.runtime.exceptions.support.ErrorType; import org.develnext.jphp.core.syntax.SyntaxAnalyzer; import org.develnext.jphp.core.tokenizer.TokenMeta; import org.develnext.jphp.core.tokenizer.TokenType; import org.develnext.jphp.core.tokenizer.Tokenizer; import org.develnext.jphp.core.tokenizer.token.Token; import org.develnext.jphp.core.tokenizer.token.expr.value.ClosureStmtToken; import org.develnext.jphp.core.tokenizer.token.expr.value.NameToken; import org.develnext.jphp.core.tokenizer.token.stmt.*; import php.runtime.reflection.helper.GeneratorEntity; import java.io.IOException; import java.util.*; public class JvmCompiler extends AbstractCompiler { protected final ModuleEntity module; protected NamespaceStmtToken namespace; private List<ClassStmtCompiler> classes = new ArrayList<ClassStmtCompiler>(); private Map<String, ConstantEntity> constants = new LinkedHashMap<String, ConstantEntity>(); private Map<String, FunctionEntity> functions = new LinkedHashMap<String, FunctionEntity>(); protected final SyntaxAnalyzer analyzer; protected final List<Token> tokens; protected YieldExprToken lastYield; public JvmCompiler(Environment environment, Context context) throws IOException { this(environment, context, new SyntaxAnalyzer(environment, new Tokenizer(context))); } public JvmCompiler(Environment environment, Context context, SyntaxAnalyzer analyzer) { super(environment, context); this.classes = new ArrayList<>(); this.module = new ModuleEntity(context); this.module.setId( scope.nextModuleIndex() ); this.tokens = analyzer.getTree(); this.analyzer = analyzer; } public SyntaxAnalyzer getAnalyzer() { return analyzer; } public ConstantEntity findConstant(String fullName){ return constants.get(fullName.toLowerCase()); } public FunctionEntity findFunction(String name){ return functions.get(name.toLowerCase()); } public NamespaceStmtToken getNamespace() { return namespace; } public void setNamespace(NamespaceStmtToken namespace) { this.namespace = namespace; } public YieldExprToken getLastYield() { return lastYield; } public void setLastYield(YieldExprToken lastYield) { this.lastYield = lastYield; } public ClassEntity compileClass(ClassStmtToken clazz, boolean external) { ClassStmtCompiler cmp = new ClassStmtCompiler(this, clazz); cmp.setExternal(external); return cmp.compile(); } public ClassEntity compileClass(ClassStmtToken clazz){ return compileClass(clazz, false); } public FunctionEntity compileFunction(FunctionStmtToken function){ FunctionStmtCompiler cmp = new FunctionStmtCompiler(this, function); return cmp.compile(); } public List<ConstantEntity> compileConstant(ConstStmtToken constant){ List<ConstantEntity> result = new ArrayList<ConstantEntity>(); for(ConstStmtToken.Item el : constant.items){ ConstantEntity constantEntity = new ConstantEntity(getContext()); constantEntity.setName(el.getFulledName()); constantEntity.setModule(module); constantEntity.setTrace(el.name.toTraceInfo(context)); ExpressionStmtCompiler expressionStmtCompiler = new ExpressionStmtCompiler(this); Memory memory = expressionStmtCompiler.writeExpression(el.value, true, true, false); if (memory == null){ throw new CompileException( Messages.ERR_EXPECTED_CONST_VALUE.fetch(el.getFulledName()), constant.toTraceInfo(context) ); } constantEntity.setValue(memory); result.add(constantEntity); } return result; } @SuppressWarnings("unchecked") public MethodEntity compileMethod(ClassStmtCompiler clazzCompiler, MethodStmtToken method, boolean external, GeneratorEntity generatorEntity) { MethodStmtCompiler compiler = new MethodStmtCompiler(clazzCompiler, method); compiler.setExternal(external); compiler.setGeneratorEntity(generatorEntity); clazzCompiler.node.methods.add(compiler.node); return compiler.compile(); } public MethodEntity compileMethod(ClassStmtCompiler clazzCompiler, MethodStmtToken method){ return compileMethod(clazzCompiler, method, false, null); } public void compileExpression(MethodStmtCompiler method, ExprStmtToken expression) { new ExpressionStmtCompiler(method, expression).compile(); } public List<ExprStmtToken> process(List<Token> tokens, NamespaceStmtToken namespace){ List<ExprStmtToken> externalCode = new ArrayList<ExprStmtToken>(); // write constants for(ConstStmtToken constant : analyzer.getConstants()){ List<ConstantEntity> items = compileConstant(constant); for(ConstantEntity el : items){ if (!constants.containsKey(el.getLowerName())){ module.addConstant(el); if (scope.findUserConstant(el.getName()) != null){ environment.error(el.getTrace(), ErrorType.E_ERROR, Messages.ERR_CANNOT_REDECLARE_CONSTANT, el.getName() ); } constants.put(el.getLowerName(), el); } else { environment.error(el.getTrace(), ErrorType.E_ERROR, Messages.ERR_CANNOT_REDECLARE_CONSTANT, el.getName() ); } } } // write closures for(ClosureStmtToken closure : analyzer.getClosures()){ ClosureEntity closureEntity = new ClosureStmtCompiler(this, closure).compile(); module.addClosure(closureEntity); } for (FunctionStmtToken function : analyzer.getFunctions()){ if (!function.isStatic()) { FunctionEntity entity = compileFunction(function); entity.setStatic(function.isStatic()); module.addFunction(entity); } } for(Token token : tokens) { if (token instanceof NamespaceStmtToken){ setNamespace((NamespaceStmtToken)token); } if (token instanceof ClassStmtToken){ ClassStmtCompiler cmp = new ClassStmtCompiler(this, (ClassStmtToken)token); ClassEntity entity = cmp.compile(); entity.setStatic(true); module.addClass(entity); if (cmp.isInitDynamicExists()){ externalCode.add(new ExprStmtToken(getEnvironment(), getContext(), new ClassInitEnvironment((ClassStmtToken)token, entity) )); } } else if (token instanceof FunctionStmtToken){ FunctionEntity entity = compileFunction((FunctionStmtToken)token); entity.setStatic(true); module.addFunction(entity); functions.put(entity.getLowerName(), entity); } else if (token instanceof ExprStmtToken){ externalCode.add((ExprStmtToken)token); } else externalCode.add(new ExprStmtToken(getEnvironment(), getContext(), token)); } return externalCode; } @Override public ModuleEntity compile(boolean autoRegister) { this.classes = new ArrayList<>(); module.setInternalName("$php_module_m" + UUID.randomUUID().toString().replace("-", "")); List<ExprStmtToken> externalCode = process(tokens, NamespaceStmtToken.getDefault()); NamespaceStmtToken namespace = NamespaceStmtToken.getDefault(); ClassStmtToken token = new ClassStmtToken(TokenMeta.of( module.getName() )); token.setFinal(true); token.setName(new NameToken(TokenMeta.of( module.getInternalName() ))); token.setNamespace(namespace); MethodStmtToken methodToken = new MethodStmtToken( token.getMeta() ); methodToken.setFinal(true); methodToken.setClazz(token); methodToken.setModifier(Modifier.PUBLIC); methodToken.setStatic(true); methodToken.setDynamicLocal(true); methodToken.setName((NameToken) Token.of("__include")); methodToken.setArguments(new ArrayList<ArgumentStmtToken>()); methodToken.setLocal(analyzer.getScope().getVariables()); methodToken.setLabels(analyzer.getScope().getLabels()); methodToken.setBody(BodyStmtToken.of(externalCode)); token.setMethods(Arrays.asList(methodToken)); ClassStmtCompiler classStmtCompiler = new ClassStmtCompiler(this, token); classStmtCompiler.setExternal(true); classStmtCompiler.setSystem(true); module.setData(classStmtCompiler.compile().getData()); // .. if (autoRegister) scope.addUserModule(module); return module; } public ModuleEntity getModule() { return module; } public List<ClassStmtCompiler> getClasses() { return classes; } public String getSourceFile() { return context.getFileName(); } public class ClassInitEnvironment extends StmtToken { protected final ClassStmtToken token; protected final ClassEntity entity; public ClassInitEnvironment(ClassStmtToken token, ClassEntity clazz) { super(token.getMeta(), TokenType.T_J_CUSTOM); this.token = token; this.entity = clazz; } public ClassStmtToken getToken() { return token; } public ClassEntity getEntity() { return entity; } } }