package org.dynjs.ir; import org.dynjs.exception.ThrowException; import org.dynjs.parser.ast.FunctionDeclaration; import org.dynjs.parser.ast.VariableDeclaration; import org.dynjs.runtime.*; import java.util.List; public class IRJSFunction extends DynObject implements JSFunction { private final FunctionScope scope; private Instruction[] instructions; private final LexicalEnvironment lexicalEnvironment; private String debugContext = ""; private SourceProvider source; // Lexically-captured values of this function private VariableValues capturedValues; public JSFunction compile(ExecutionContext context) { return new IRByteCodeCompiler(scope, getFileName(), isStrict()).compileFunction(context); } private static class IRJSFunctionBox { public int callCount = 0; public JSCallable compiledFunction; public boolean compilationInProgress; } private IRJSFunctionBox box = new IRJSFunctionBox(); public IRJSFunction(FunctionScope scope, VariableValues capturedValues, LexicalEnvironment lexicalEnvironment, GlobalContext globalContext) { super(globalContext); this.scope = scope; this.instructions = scope.prepareForInterpret(); // FIXME This is a big up front cost...make lazy this.lexicalEnvironment = lexicalEnvironment; this.capturedValues = capturedValues; } public String[] getFormalParameters() { return scope.getParameterNames(); } public LexicalEnvironment getScope() { return lexicalEnvironment; } @Override public String getFileName() { return scope.getFileName(); } // FIXME: Not sure but constructor is likely a different beast than IRJSFunction public boolean isConstructor() { return false; } public String getDebugContext() { return debugContext; } public void setDebugContext(String debugContext) { this.debugContext = debugContext; } @Override public SourceProvider getSource() { return this.source; } @Override public void setSource(SourceProvider source) { this.source = source; } public JSObject createNewObject(ExecutionContext context) { return new DynObject(context.getGlobalContext()); } // FIXME: Stolen from AbstractionFunction (could be refactored out unless we can somehow have whatever calls this // replaced by IR (I have no idea) public boolean hasInstance(ExecutionContext context, Object v) { if (!(v instanceof JSObject)) { return false; } Object o = get(null, "prototype"); if (!(o instanceof JSObject)) { throw new ThrowException(context, context.createTypeError("prototype must be an object")); } JSObject proto = (JSObject) o; if (proto == null || v == Types.UNDEFINED) { return false; } while (true) { v = ((JSObject) v).getPrototype(); if (v == null || v == Types.UNDEFINED) { return false; } if (v == proto) { return true; } } } @Override public Object call(ExecutionContext context) { // Allocate space for variables of this function and establish link to captured ones. context.allocVars(scope.getLocalVariableSize(), capturedValues); if (box.compilationInProgress || box.callCount >= 0) { if (tryCompile(context)) { return callJitted(context); } } return Interpreter.execute(context, scope, instructions); } private Object callJitted(ExecutionContext context) { return box.compiledFunction.call(context); } private boolean tryCompile(ExecutionContext context) { if (box.compiledFunction != null) { return true; } if (box.callCount++ >= context.getConfig().getJitThreshold()) { box.callCount = -1; // disable, we get one shot if (!box.compilationInProgress) { if (context.getConfig().isJitEnabled()) { final JITCompiler compiler = context.getRuntime().getJitCompiler(); box.compilationInProgress = true; compiler.compile(context, this, new JITCompiler.CompilerCallback() { @Override public void done(JSFunction compiledFunction) { box.compiledFunction = compiledFunction; } }); } } } return false; } @Override public boolean isStrict() { return scope.isStrict(); } // FIXME: Remove or replace once we learn how IR should handle these @Override public List<FunctionDeclaration> getFunctionDeclarations() { return FunctionDeclaration.EMPTY_LIST; } // FIXME: Remove or replace once we learn how IR should handle these @Override public List<VariableDeclaration> getVariableDeclarations() { return VariableDeclaration.EMPTY_LIST; } }