package org.dynjs.codegen;
import static me.qmx.jitescript.util.CodegenUtils.*;
import java.util.List;
import me.qmx.jitescript.CodeBlock;
import org.dynjs.exception.ThrowException;
import org.dynjs.parser.ast.AssignmentExpression;
import org.dynjs.parser.ast.Expression;
import org.dynjs.parser.ast.FunctionCallExpression;
import org.dynjs.parser.ast.NewOperatorExpression;
import org.dynjs.parser.ast.VariableDeclaration;
import org.dynjs.runtime.BlockManager;
import org.dynjs.runtime.EnvironmentRecord;
import org.dynjs.runtime.ExecutionContext;
import org.dynjs.runtime.JSObject;
import org.dynjs.runtime.Reference;
import org.dynjs.runtime.interp.InterpretingVisitorFactory;
import org.dynjs.runtime.linker.DynJSBootstrapper;
import me.qmx.jitescript.internal.org.objectweb.asm.tree.LabelNode;
public class InvokeDynamicBytecodeGeneratingVisitor extends BasicBytecodeGeneratingVisitor {
public InvokeDynamicBytecodeGeneratingVisitor(InterpretingVisitorFactory interpFactory, BlockManager blockManager) {
super(interpFactory, blockManager);
}
@Override
public Object visit(Object context, VariableDeclaration expr, boolean strict) {
if (expr.getExpr() == null) {
ldc(expr.getIdentifier());
// str
} else {
append(jsResolve(expr.getIdentifier()));
// reference
aload(Arities.EXECUTION_CONTEXT);
// reference context
ldc(expr.getIdentifier());
// reference context name
expr.getExpr().accept(context, this, strict);
// reference context name val
append(jsGetValue());
// reference context name val
invokedynamic("dyn:setProperty", sig(void.class, Object.class, ExecutionContext.class, String.class, Object.class), DynJSBootstrapper.HANDLE,
DynJSBootstrapper.ARGS);
// <empty>
ldc(expr.getIdentifier());
// str
}
return null;
}
@Override
public Object visit(Object context, AssignmentExpression expr, boolean strict) {
LabelNode throwRefError = new LabelNode();
LabelNode end = new LabelNode();
expr.getLhs().accept(context, this, strict);
// ref
dup();
// ref ref
instance_of(p(Reference.class));
// ref bool
iffalse(throwRefError);
// ref
checkcast(p(Reference.class));
// ref
expr.getRhs().accept(context, this, strict);
// ref expr
append(jsGetValue());
// ref value
dup_x1();
// value ref value
swap();
// value value ref
dup_x1();
// value ref value ref
invokevirtual(p(Reference.class), "getReferencedName", sig(String.class));
// value ref value name
dup_x1();
// value ref name value name
pop();
// value ref name value
aload(Arities.EXECUTION_CONTEXT);
// value ref name value context
dup_x2();
// value ref context name value context
pop();
// value ref context name value
invokedynamic("dyn:setProperty", sig(void.class, Object.class, ExecutionContext.class, String.class, Object.class), DynJSBootstrapper.HANDLE,
DynJSBootstrapper.ARGS);
// value
go_to(end);
label(throwRefError);
// reference
pop();
newobj(p(ThrowException.class));
// ex
dup();
// ex ex
aload(Arities.EXECUTION_CONTEXT);
// ex ex context
ldc(expr.getLhs().toString() + " is not a reference");
// ex ex context str
invokevirtual(p(ExecutionContext.class), "createReferenceError", sig(JSObject.class, String.class));
// ex ex error
aload(Arities.EXECUTION_CONTEXT);
// ex ex error context
swap();
// ex ex context error
invokespecial(p(ThrowException.class), "<init>", sig(void.class, ExecutionContext.class, Object.class));
// ex ex
athrow();
label(end);
nop();
return null;
}
@Override
public Object visit(Object context, NewOperatorExpression expr, boolean strict) {
LabelNode end = new LabelNode();
// 11.2.2
expr.getExpr().accept(context, this, strict);
// obj
aload(Arities.EXECUTION_CONTEXT);
// obj context
swap();
// context obj
append(jsGetValue());
// context ctor-fn
swap();
// ctor-fn context
List<Expression> argExprs = expr.getArgumentExpressions();
int numArgs = argExprs.size();
bipush(numArgs);
anewarray(p(Object.class));
// ctor-fn context array
for (int i = 0; i < numArgs; ++i) {
dup();
bipush(i);
argExprs.get(i).accept(context, this, strict);
append(jsGetValue());
aastore();
}
// ctor-fn context array
invokedynamic("dyn:construct", sig(Object.class, Object.class, ExecutionContext.class, Object[].class), DynJSBootstrapper.HANDLE, DynJSBootstrapper.ARGS);
// obj
label(end);
nop();
return null;
}
@Override
public Object visit(Object context, FunctionCallExpression expr, boolean strict) {
LabelNode propertyRef = new LabelNode();
LabelNode noSelf = new LabelNode();
LabelNode doCall = new LabelNode();
// 11.2.3
expr.getMemberExpression().accept(context, this, strict);
// fnexpr
dup();
// fnexpr fnexpr
/*
* aload(Arities.EXECUTION_CONTEXT);
* fnexpr fnexpr context
* invokestatic(p(DereferencedReference.class), "create", sig(Object.class, Object.class, Object.class));
*/
dup();
// fnexpr fnexpr fnexpr
append(jsGetValue());
// fnexpr fn-ref fn
invokestatic(p(DereferencedReference.class), "create", sig(Object.class, Object.class, Object.class));
// fnexpr fn
swap();
// fn fnexpr
dup();
// fn fnexpr fnexpr
instance_of(p(Reference.class));
// fn fnexpr isref?
iffalse(noSelf);
// ----------------------------------------
// Reference
// fn ref
checkcast(p(Reference.class));
// fn ref
dup();
// fn ref ref
invokevirtual(p(Reference.class), "isPropertyReference", sig(boolean.class));
// fn ref bool(is-prop)
iftrue(propertyRef);
// ----------------------------------------
// Environment Record
// fn ref
invokevirtual(p(Reference.class), "getBase", sig(Object.class));
// fn base
checkcast(p(EnvironmentRecord.class));
// fn env-rec
invokeinterface(p(EnvironmentRecord.class), "implicitThisValue", sig(Object.class));
// fn self
go_to(doCall);
// ----------------------------------------
// Property Reference
label(propertyRef);
// fn ref
append(jsGetBase());
// fn self
go_to(doCall);
// ------------------------------------------
// No self
label(noSelf);
// fn fnexpr
pop();
// fn
append(jsPushUndefined());
// fn UNDEFINED
// ------------------------------------------
// call()
label(doCall);
// fn self
aload(Arities.EXECUTION_CONTEXT);
// fn self context
swap();
// fn context self
List<Expression> argExprs = expr.getArgumentExpressions();
int numArgs = argExprs.size();
bipush(numArgs);
anewarray(p(Object.class));
// fn context self array
for (int i = 0; i < numArgs; ++i) {
dup();
bipush(i);
argExprs.get(i).accept(context, this, strict);
append(jsGetValue());
aastore();
}
// fn context self array
// function context ref self args
invokedynamic("dyn:call", sig(Object.class, Object.class, ExecutionContext.class, Object.class, Object[].class), DynJSBootstrapper.HANDLE,
DynJSBootstrapper.ARGS);
// value
return null;
}
/*
* @Override
* public void visit(ExecutionContext context, AssignmentExpression expr, boolean strict) {
* LabelNode throwRefError = new LabelNode();
* LabelNode end = new LabelNode();
*
* LabelNode isUnresolvableRef = new LabelNode();
* LabelNode isPropertyRef = new LabelNode();
* LabelNode isEnvRecord = new LabelNode();
* LabelNode doPut = new LabelNode();
*
* expr.getLhs().accept(context, this, strict);
* // reference
* dup();
* // reference reference
* instance_of(p(Reference.class));
* // reference bool
* iffalse(throwRefError);
* // reference
* checkcast(p(Reference.class));
* // ref
* dup();
* // ref ref
* invokevirtual(p(Reference.class), "isUnresolvableReference", sig(boolean.class));
* // ref unresolv?
* iftrue(isUnresolvableRef);
* // ref
* dup();
* // ref ref
* invokevirtual(p(Reference.class), "isPropertyReference", sig(boolean.class));
* // ref isprop?
* iftrue(isPropertyRef);
* // ref
* go_to(isEnvRecord);
*
* // ----------------------------------------
* // unresolvable ref
* // ----------------------------------------
*
* label(isUnresolvableRef);
* // ref
* dup();
* // ref ref
* invokevirtual(p(Reference.class), "isStrictReference", sig(boolean.class));
* // ref isstrict?
* iftrue(throwRefError);
* // ref
* aload(Arities.EXECUTION_CONTEXT);
* // ref context
* invokevirtual(p(ExecutionContext.class), "getGlobalContext", sig(GlobalObject.class));
* // ref obj
* go_to(doPut);
*
* // ----------------------------------------
* // property ref
* // ----------------------------------------
*
* label( isPropertyRef );
* // ref
* dup();
* // ref ref
* invokevirtual(p(Reference.class), "getBase", sig(Object.class));
* // ref obj
* go_to( doPut );
*
* // ----------------------------------------
* // property ref
* // ----------------------------------------
*
* label( isEnvRecord );
* // ref
* dup();
* // ref ref
* invokevirtual(p(Reference.class), "getBase", sig(Object.class));
* // ref obj
* go_to( doPut );
*
*
* label( doPut );
* // ref obj
* swap();
* // obj ref
* dup();
* // obj ref ref
* aload(Arities.EXECUTION_CONTEXT);
* // obj ref ref context
* invokestatic(p(ReferenceContext.class), "create", sig(ReferenceContext.class, Reference.class, ExecutionContext.class));
* // obj ref context
* swap();
* // obj context ref
* invokevirtual(p(Reference.class), "getReferencedName", sig(String.class));
* // obj context name
* expr.getRhs().accept(context, this, strict);
* // obj context name value
* append(jsGetValue());
* // object context name value
* invokedynamic("dyn:setProp", sig(Object.class, Object.class, ReferenceContext.class, String.class, Object.class), DynJSBootstrapper.HANDLE, DynJSBootstrapper.ARGS);
* // value
* go_to(end);
*
* label(throwRefError);
* // reference
* pop();
*
* newobj(p(ThrowException.class));
* // ex
* dup();
* // ex ex
* aload(Arities.EXECUTION_CONTEXT);
* // ex ex context
* ldc(expr.getLhs().toString() + " is not a reference");
* // ex ex context str
* invokevirtual(p(ExecutionContext.class), "createReferenceError", sig(JSObject.class, String.class));
* // ex ex error
* aload(Arities.EXECUTION_CONTEXT);
* // ex ex error context
* swap();
* // ex ex context error
* invokespecial(p(ThrowException.class), "<init>", sig(void.class, ExecutionContext.class, Object.class));
* // ex ex
* athrow();
*
* label(end);
* nop();
*
* }
*/
@Override
public CodeBlock jsGetValue(final Class<?> throwIfNot) {
LabelNode end = new LabelNode();
LabelNode throwRef = new LabelNode();
CodeBlock codeBlock = new CodeBlock()
// IN: reference
.dup()
// ref ref
.instance_of(p(Reference.class))
// ref isref?
.iffalse(end)
.checkcast(p(Reference.class))
// ref
.dup()
// ref ref
.invokevirtual(p(Reference.class), "isUnresolvableReference", sig(boolean.class))
// ref unresolv?
.iftrue(throwRef)
// ref
.dup()
// ref ref
.invokevirtual(p(Reference.class), "getReferencedName", sig(String.class))
// ref name
.aload(Arities.EXECUTION_CONTEXT)
// ref name context
.swap()
// ref context name
.invokedynamic("dyn:getProperty|getMethod", sig(Object.class, Object.class, ExecutionContext.class, String.class), DynJSBootstrapper.HANDLE,
DynJSBootstrapper.ARGS);
// value
if (throwIfNot != null) {
codeBlock.dup()
// value value
.instance_of(p(throwIfNot))
// value bool
.iftrue(end)
// value
.pop()
.append(jsThrowTypeError("expected " + throwIfNot.getName()));
}
// result
codeBlock.go_to(end)
.label(throwRef)
.append(jsThrowReferenceError("unable to dereference"))
.label(end)
// value
.nop();
return codeBlock;
}
}