package org.dynjs.compiler.bytecode; import static me.qmx.jitescript.util.CodegenUtils.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import me.qmx.jitescript.CodeBlock; import me.qmx.jitescript.JiteClass; import org.dynjs.Config; import org.dynjs.codegen.CodeGeneratingVisitor.Arities; import org.dynjs.codegen.CodeGeneratingVisitorFactory; import org.dynjs.compiler.BasicBlockCompiler; import org.dynjs.compiler.CompilationContext; import org.dynjs.compiler.bytecode.partial.CompilationPlanner; import org.dynjs.compiler.bytecode.partial.PartialCompiler; import org.dynjs.parser.Statement; import org.dynjs.parser.ast.BlockStatement; import org.dynjs.parser.js.Position; import org.dynjs.runtime.BasicBlock; import org.dynjs.runtime.BlockManager.Entry; import org.dynjs.runtime.DynamicClassLoader; import org.dynjs.runtime.ExecutionContext; import me.qmx.jitescript.internal.org.objectweb.asm.Opcodes; public class BytecodeBasicBlockCompiler extends AbstractBytecodeCompiler implements BasicBlockCompiler { private AtomicInteger counter = new AtomicInteger(); public BytecodeBasicBlockCompiler(Config config, CodeGeneratingVisitorFactory factory) { super(config, factory); } @Override public BasicBlock compile(final CompilationContext context, final String grist, final Statement body, boolean strict) { int statementNumber = body.getStatementNumber(); Entry entry = context.getBlockManager().retrieve(statementNumber); BasicBlock code = entry.getCompiled(); if ( code instanceof BytecodeBasicBlock ) { return code; } String className = nextClassName(grist); final JiteClass cls = new JiteClass(className, p(BytecodeBasicBlock.class), new String[] {}); cls.defineMethod("<init>", Opcodes.ACC_PUBLIC, sig(void.class, String.class, boolean.class, List.class, List.class ), new CodeBlock() .aload(Arities.THIS) // this .aload(1) // this filename .iload(2) // this filename strict .aload(3) // this filename strict vardecls .aload(4) // this filename strict vardecls fndecls .invokespecial(p(BytecodeBasicBlock.class), "<init>", sig(void.class, String.class, boolean.class, List.class, List.class) ) // <empty> .aload( Arities.THIS ) // this .invokevirtual(cls.getClassName().replace('.', '/'), "initializeCode", sig(void.class)) // <empty> .voidreturn() ); CompilationPlanner planner = new CompilationPlanner(getConfig(), context.getClassLoader(), getFactory()); PartialCompiler compiler = null; if (body instanceof BlockStatement) { compiler = planner.plan((BlockStatement) body); } else { compiler = planner.plan(new BlockStatement(Collections.singletonList(body))); } compiler.define(cls, context, false); Class<BytecodeBasicBlock> blockClass = defineClass(context.getClassLoader(), cls); Position position = body.getPosition(); String fileName = (position != null ? position.getFileName() : "eval"); try { Constructor<BytecodeBasicBlock> ctor = blockClass.getDeclaredConstructor(String.class, boolean.class, List.class, List.class); BasicBlock block = ctor.newInstance(fileName, strict, body.getVariableDeclarations(), body.getFunctionDeclarations()); entry.setCompiled(block); return block; } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new IllegalStateException(e); } } public String nextClassName(String grist) { return getConfig().getBasePackage().replace('.', '/') + "/" + grist + nextCounterValue(); } private int nextCounterValue() { return this.counter.getAndIncrement(); } }