/*
* Copyright 2014 JBoss Inc
*
* 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.
*/
package org.dynjs.ir;
import org.dynjs.compiler.CompilationContext;
import org.dynjs.ir.instructions.Add;
import org.dynjs.ir.instructions.BEQ;
import org.dynjs.ir.instructions.Call;
import org.dynjs.ir.instructions.Constructor;
import org.dynjs.ir.instructions.Copy;
import org.dynjs.ir.instructions.DefineFunction;
import org.dynjs.ir.instructions.Instanceof;
import org.dynjs.ir.instructions.Jump;
import org.dynjs.ir.instructions.LE;
import org.dynjs.ir.instructions.LT;
import org.dynjs.ir.instructions.LabelInstr;
import org.dynjs.ir.instructions.Mul;
import org.dynjs.ir.instructions.PropertyLookup;
import org.dynjs.ir.instructions.Raise;
import org.dynjs.ir.instructions.ReceiveFunctionParameter;
import org.dynjs.ir.instructions.Return;
import org.dynjs.ir.instructions.Sub;
import org.dynjs.ir.operands.BooleanLiteral;
import org.dynjs.ir.operands.DynamicVariable;
import org.dynjs.ir.operands.FloatNumber;
import org.dynjs.ir.operands.IntegerNumber;
import org.dynjs.ir.operands.Label;
import org.dynjs.ir.operands.Null;
import org.dynjs.ir.operands.StringLiteral;
import org.dynjs.ir.operands.TemporaryVariable;
import org.dynjs.ir.operands.This;
import org.dynjs.ir.operands.Undefined;
import org.dynjs.ir.operands.Variable;
import org.dynjs.parser.CodeVisitor;
import org.dynjs.parser.Statement;
import org.dynjs.parser.ast.AdditiveExpression;
import org.dynjs.parser.ast.ArrayLiteralExpression;
import org.dynjs.parser.ast.AssignmentExpression;
import org.dynjs.parser.ast.BitwiseExpression;
import org.dynjs.parser.ast.BitwiseInversionOperatorExpression;
import org.dynjs.parser.ast.BlockStatement;
import org.dynjs.parser.ast.BooleanLiteralExpression;
import org.dynjs.parser.ast.BracketExpression;
import org.dynjs.parser.ast.BreakStatement;
import org.dynjs.parser.ast.CaseClause;
import org.dynjs.parser.ast.CatchClause;
import org.dynjs.parser.ast.CommaOperator;
import org.dynjs.parser.ast.CompoundAssignmentExpression;
import org.dynjs.parser.ast.ContinueStatement;
import org.dynjs.parser.ast.DefaultCaseClause;
import org.dynjs.parser.ast.DeleteOpExpression;
import org.dynjs.parser.ast.DoWhileStatement;
import org.dynjs.parser.ast.DotExpression;
import org.dynjs.parser.ast.EmptyStatement;
import org.dynjs.parser.ast.EqualityOperatorExpression;
import org.dynjs.parser.ast.Expression;
import org.dynjs.parser.ast.ExpressionStatement;
import org.dynjs.parser.ast.FloatingNumberExpression;
import org.dynjs.parser.ast.ForExprInStatement;
import org.dynjs.parser.ast.ForExprOfStatement;
import org.dynjs.parser.ast.ForExprStatement;
import org.dynjs.parser.ast.ForVarDeclInStatement;
import org.dynjs.parser.ast.ForVarDeclOfStatement;
import org.dynjs.parser.ast.ForVarDeclStatement;
import org.dynjs.parser.ast.FunctionCallExpression;
import org.dynjs.parser.ast.FunctionDeclaration;
import org.dynjs.parser.ast.FunctionDescriptor;
import org.dynjs.parser.ast.FunctionExpression;
import org.dynjs.parser.ast.IdentifierReferenceExpression;
import org.dynjs.parser.ast.IfStatement;
import org.dynjs.parser.ast.IllegalFunctionMemberExpression;
import org.dynjs.parser.ast.InOperatorExpression;
import org.dynjs.parser.ast.InstanceofExpression;
import org.dynjs.parser.ast.IntegerNumberExpression;
import org.dynjs.parser.ast.LogicalExpression;
import org.dynjs.parser.ast.LogicalNotOperatorExpression;
import org.dynjs.parser.ast.MultiplicativeExpression;
import org.dynjs.parser.ast.NamedValue;
import org.dynjs.parser.ast.NewOperatorExpression;
import org.dynjs.parser.ast.NullLiteralExpression;
import org.dynjs.parser.ast.ObjectLiteralExpression;
import org.dynjs.parser.ast.OfOperatorExpression;
import org.dynjs.parser.ast.PostOpExpression;
import org.dynjs.parser.ast.PreOpExpression;
import org.dynjs.parser.ast.ProgramTree;
import org.dynjs.parser.ast.PropertyGet;
import org.dynjs.parser.ast.PropertySet;
import org.dynjs.parser.ast.RegexpLiteralExpression;
import org.dynjs.parser.ast.RelationalExpression;
import org.dynjs.parser.ast.ReturnStatement;
import org.dynjs.parser.ast.StrictEqualityOperatorExpression;
import org.dynjs.parser.ast.StringLiteralExpression;
import org.dynjs.parser.ast.SwitchStatement;
import org.dynjs.parser.ast.TernaryExpression;
import org.dynjs.parser.ast.ThisExpression;
import org.dynjs.parser.ast.ThrowStatement;
import org.dynjs.parser.ast.TryStatement;
import org.dynjs.parser.ast.TypeOfOpExpression;
import org.dynjs.parser.ast.UnaryMinusExpression;
import org.dynjs.parser.ast.UnaryPlusExpression;
import org.dynjs.parser.ast.VariableDeclaration;
import org.dynjs.parser.ast.VariableStatement;
import org.dynjs.parser.ast.VoidOperatorExpression;
import org.dynjs.parser.ast.WhileStatement;
import org.dynjs.parser.ast.WithStatement;
import org.dynjs.runtime.JSProgram;
import java.util.List;
public class Builder implements CodeVisitor {
private static Builder BUILDER = new Builder();
public static JSProgram compile(CompilationContext compilationContext, ProgramTree program) {
Scope scope = new Scope(null, program.getPosition().getFileName(), program.isStrict());
program.accept(scope, BUILDER, program.isStrict());
// FIXME: Add processing stage here/somewhere to do instr process/cfg/passes.
return new IRJSProgram(program.getSource(), compilationContext.getBlockManager(), scope);
}
@Override
public Object visit(Object context, AdditiveExpression expr, boolean strict) {
Scope scope = (Scope) context;
Operand lhs = (Operand) expr.getLhs().accept(context, this, strict);
Operand rhs = (Operand) expr.getRhs().accept(context, this, strict);
boolean subtract = expr.getOp().equals("-");
Operand value;
// FIXME: Review numeric representation of JS to figure out overflow etc..
if (lhs instanceof IntegerNumber) {
if (rhs instanceof IntegerNumber) {
if (subtract) {
value = new IntegerNumber(((IntegerNumber) lhs).getValue() - ((IntegerNumber) rhs).getValue());
} else {
value = new IntegerNumber(((IntegerNumber) lhs).getValue() + ((IntegerNumber) rhs).getValue());
}
} else if (rhs instanceof FloatNumber) {
if (subtract) {
value = new FloatNumber(((FloatNumber) rhs).getValue() - ((IntegerNumber) lhs).getValue());
} else {
value = new FloatNumber(((FloatNumber) rhs).getValue() + ((IntegerNumber) lhs).getValue());
}
} else {
Variable tmp = scope.createTemporaryVariable();
if (subtract) {
scope.addInstruction(new Sub(tmp, lhs, rhs));
} else {
scope.addInstruction(new Add(tmp, lhs, rhs));
}
value = tmp;
}
} else {
Variable tmp = scope.createTemporaryVariable();
if (subtract) {
scope.addInstruction(new Sub(tmp, lhs, rhs));
} else {
scope.addInstruction(new Add(tmp, lhs, rhs));
}
value = tmp;
}
return value;
}
@Override
public Object visit(Object context, BitwiseExpression bitwiseExpression, boolean strict) {
return unimplemented(context, bitwiseExpression, strict);
}
@Override
public Object visit(Object context, ArrayLiteralExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, AssignmentExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable lhs = (Variable) expr.getLhs().accept(context, this, strict);
Operand rhs = (Operand) expr.getRhs().accept(context, this, strict);
scope.addInstruction(new Copy(lhs, rhs));
return rhs;
}
@Override
public Object visit(Object context, BitwiseInversionOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, BlockStatement block, boolean strict) {
Scope scope = (Scope) context;
buildDeclaredFunctions(scope, block.getFunctionDeclarations());
// FIXME: How can we use what the parser provides to good effect?
Operand value = Undefined.UNDEFINED;
for (Statement statement: block.getBlockContent()) {
value = (Operand) statement.accept(context, this, strict);
}
return value;
}
@Override
public Object visit(Object context, BooleanLiteralExpression expr, boolean strict) {
return expr.getValue() ? BooleanLiteral.TRUE : BooleanLiteral.FALSE;
}
@Override
public Object visit(Object context, BreakStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, CaseClause clause, boolean strict) {
return unimplemented(context, clause, strict);
}
@Override
public Object visit(Object context, DefaultCaseClause clause, boolean strict) {
return unimplemented(context, clause, strict);
}
@Override
public Object visit(Object context, CatchClause clause, boolean strict) {
return unimplemented(context, clause, strict);
}
@Override
public Object visit(Object context, CompoundAssignmentExpression expr, boolean strict) {
Scope scope = (Scope) context;
// FIXME: If name of lhs is 'eval' or 'assignments' then generate a raise error instance of doing copy.
// This is a little out of order with basicinterp where we check this before eval'ing value below.
// Of s += 1 this is (s + 1)
Operand value = (Operand) expr.getRootExpr().accept(context, this, strict);
Variable lhs = (Variable) expr.getRootExpr().getLhs().accept(context, this, strict);
scope.addInstruction(new Copy(lhs, value));
return value;
}
@Override
public Object visit(Object context, ContinueStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, DeleteOpExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, DoWhileStatement statement, boolean strict) {
Scope scope = (Scope) context;
final Label startLabel = scope.getNewLabel();
final Label doneLabel = scope.getNewLabel();
scope.addInstruction(new LabelInstr(startLabel));
// BODY
statement.getBlock().accept(context, this, strict);
// TEST
scope.addInstruction(new BEQ((Operand) statement.getTest().accept(context, this, strict),
BooleanLiteral.FALSE, doneLabel));
scope.addInstruction(new Jump(startLabel));
// END
scope.addInstruction(new LabelInstr(doneLabel));
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, EmptyStatement statement, boolean strict) {
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, EqualityOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, CommaOperator expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, ExpressionStatement statement, boolean strict) {
return acceptOrUndefined(context, statement.getExpr(), strict);
}
@Override
public Object visit(Object context, FloatingNumberExpression expr, boolean strict) {
return new FloatNumber(expr.getValue());
}
@Override
public Object visit(Object context, ForExprInStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, ForExprOfStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, ForExprStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, ForVarDeclInStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, ForVarDeclOfStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
// FIXME: flow control may or may not be an issue but old runtime handles it explicitly after accept.
@Override
public Object visit(Object context, ForVarDeclStatement statement, boolean strict) {
Scope scope = (Scope) context;
List<VariableDeclaration> decls = statement.getDeclarationList();
for (VariableDeclaration each : decls) {
each.accept(context, this, strict);
}
final Label startLabel = scope.getNewLabel();
final Label doneLabel = scope.getNewLabel();
scope.addInstruction(new LabelInstr(startLabel));
// TEST
scope.addInstruction(new BEQ((Operand) statement.getTest().accept(context, this, strict),
BooleanLiteral.FALSE, doneLabel));
// BODY
statement.getBlock().accept(context, this, strict);
// INCREMENT
statement.getIncrement().accept(context, this, strict);
scope.addInstruction(new Jump(startLabel));
// END
scope.addInstruction(new LabelInstr(doneLabel));
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, FunctionCallExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable result = scope.createTemporaryVariable();
List<Expression> argumentExpressions = expr.getArgumentExpressions();
int argsLength = argumentExpressions.size();
Operand[] args = new Operand[argsLength];
for (int i = 0; i < argsLength; i++) {
args[i] = (Operand) acceptOrUndefined(context, argumentExpressions.get(i), strict);
}
Expression memberExpression = expr.getMemberExpression();
// We can statically replace an attempt at calls to illegal member types with an exception
// raise. This should eliminate needing to actually check this within the runtime.
if (memberExpression instanceof IllegalFunctionMemberExpression) {
scope.addInstruction(new Raise("TypeError", memberExpression + " is not callable"));
return Undefined.UNDEFINED;
}
final Operand name = (Operand) acceptOrUndefined(context, expr.getMemberExpression(), strict);
scope.addInstruction(new Call(result, This.THIS, name, args));
return result;
}
@Override
public Object visit(Object context, FunctionDeclaration statement, boolean strict) {
// We need to be able to define function decl before any code in the block
// they live in is executed so when we see them inline while building instructions
// we just ignore them.
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, FunctionExpression expr, boolean strict) {
Scope scope = (Scope) context;
FunctionDescriptor descriptor = expr.getDescriptor();
Variable result = scope.createTemporaryVariable();
String[] parameterNames = descriptor.getFormalParameterNames();
FunctionScope functionScope = new FunctionScope(scope, descriptor.getPosition().getFileName(),
descriptor.isStrict(), parameterNames, null);
// 1. receive arguments are first before any mandatory items like nested functions
// Recieve all declared parameters
int paramsLength = parameterNames.length;
for (int i = 0; i < paramsLength; i++) {
functionScope.addInstruction(new ReceiveFunctionParameter(functionScope.acquireLocalVariable(parameterNames[i]), i));
}
// 2. named functions must be visible before any code in the block executed so right after parms
buildDeclaredFunctions(functionScope, descriptor.getBlock().getFunctionDeclarations());
// 3. the actual block code.
descriptor.getBlock().accept(functionScope, this, strict);
scope.addInstruction(new DefineFunction(result, functionScope));
return result;
}
private void buildDeclaredFunctions(Scope parentScope, List<FunctionDeclaration> functionDeclarations) {
// ENEBO: reusing the temp var here since it is not actually used.
Variable result = parentScope.createTemporaryVariable();
for (FunctionDeclaration declaration: functionDeclarations) {
String[] parameterNames = declaration.getFormalParameters();
FunctionScope functionScope = new FunctionScope(parentScope, declaration.getPosition().getFileName(),
declaration.isStrict(), declaration.getFormalParameters(), declaration.getIdentifier());
// 1. receive arguments are first before any mandatory items like nested functions
// Recieve all declared parameters
int paramsLength = parameterNames.length;
for (int i = 0; i < paramsLength; i++) {
functionScope.addInstruction(new ReceiveFunctionParameter(functionScope.acquireLocalVariable(parameterNames[i]), i));
}
// 2. named functions must be visible before any code in the block executed so right after parms
buildDeclaredFunctions(functionScope, declaration.getBlock().getFunctionDeclarations());
// 3. the actual block code.
declaration.getBlock().accept(functionScope, this, declaration.isStrict());
parentScope.addInstruction(new DefineFunction(result, functionScope));
}
}
@Override
public Object visit(Object context, IdentifierReferenceExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable variable = scope.findVariable(expr.getIdentifier());
// FIXME: Should this error out in strict mode?
if (variable == null) {
return new DynamicVariable(expr.getIdentifier());
}
return variable;
}
@Override
public Object visit(Object context, IfStatement ifNode, boolean strict) {
Scope scope = (Scope) context;
Label elseLabel = scope.getNewLabel();
Label doneLabel = scope.getNewLabel();
// IF
scope.addInstruction(new BEQ((Operand) ifNode.getTest().accept(context, this, strict),
BooleanLiteral.FALSE, elseLabel));
// THEN
ifNode.getThenBlock().accept(context, this, strict);
scope.addInstruction(new Jump(doneLabel));
// ELSE
scope.addInstruction(new LabelInstr(elseLabel));
if (ifNode.getElseBlock() != null) {
ifNode.getElseBlock().accept(context, this, strict);
}
// END
scope.addInstruction(new LabelInstr(doneLabel));
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, InOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, OfOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, InstanceofExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable result = scope.createTemporaryVariable();
Operand lhs = (Variable) expr.getLhs().accept(context, this, strict);
Operand rhs = (Operand) expr.getRhs().accept(context, this, strict);
scope.addInstruction(new Instanceof(result, lhs, rhs));
return result;
}
@Override
public Object visit(Object context, IntegerNumberExpression expr, boolean strict) {
return new IntegerNumber(expr.getValue());
}
@Override
public Object visit(Object context, LogicalExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, LogicalNotOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, DotExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable result = scope.createTemporaryVariable();
Operand base = (Operand) expr.getLhs().accept(context, this, strict);
scope.addInstruction(new PropertyLookup(result, base, expr.getIdentifier()));
return result;
}
@Override
public Object visit(Object context, BracketExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, MultiplicativeExpression expr, boolean strict) {
Scope scope = (Scope) context;
Operand lhs = (Operand) expr.getLhs().accept(context, this, strict);
Operand rhs = (Operand) expr.getRhs().accept(context, this, strict);
Operand value;
// FIXME: Review numeric representation of JS to figure out overflow etc..
if (lhs instanceof IntegerNumber) {
if (rhs instanceof IntegerNumber) {
value = new IntegerNumber(((IntegerNumber) lhs).getValue() * ((IntegerNumber) rhs).getValue());
} else if (rhs instanceof FloatNumber) {
value = new FloatNumber(((FloatNumber) rhs).getValue() * ((IntegerNumber) lhs).getValue());
} else {
Variable tmp = scope.createTemporaryVariable();
scope.addInstruction(new Mul(tmp, lhs, rhs));
value = tmp;
}
} else {
Variable tmp = scope.createTemporaryVariable();
scope.addInstruction(new Mul(tmp, lhs, rhs));
value = tmp;
}
return value;
}
@Override
public Object visit(Object context, NewOperatorExpression expr, boolean strict) {
Scope scope = (Scope) context;
// We can statically replace an attempt at calls to illegal member types with an exception
// raise. This should eliminate needing to actually check this within the runtime.
if (expr.getExpr() instanceof IllegalFunctionMemberExpression) {
scope.addInstruction(new Raise("TypeError", expr.getExpr() + " is not callable"));
return Undefined.UNDEFINED;
}
Operand memberExpression = (Operand) expr.getExpr().accept(context, this, strict);
Variable tmp = scope.createTemporaryVariable();
List<Expression> argumentExpressions = expr.getArgumentExpressions();
Operand args[] = new Operand[argumentExpressions.size()];
for (int i = 0; i < args.length; i++) {
args[i] = (Operand) argumentExpressions.get(i).accept(context, this, strict);
}
scope.addInstruction(new Constructor(tmp, memberExpression, args));
return tmp;
}
@Override
public Object visit(Object context, NullLiteralExpression expr, boolean strict) {
return Null.NULL;
}
@Override
public Object visit(Object context, ObjectLiteralExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, PostOpExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable tmp = scope.createTemporaryVariable();
Variable variable = (Variable) expr.getExpr().accept(context, this, strict);
boolean subtract = expr.getOp().equals("-");
if (subtract) {
scope.addInstruction(new Sub(tmp, variable, new IntegerNumber(1)));
} else {
scope.addInstruction(new Add(tmp, variable, new IntegerNumber(1)));
}
scope.addInstruction(new Copy(variable, tmp));
return variable;
}
@Override
public Object visit(Object context, PreOpExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, PropertyGet propertyGet, boolean strict) {
return unimplemented(context, propertyGet, strict);
}
@Override
public Object visit(Object context, PropertySet propertySet, boolean strict) {
return unimplemented(context, propertySet, strict);
}
@Override
public Object visit(Object context, NamedValue namedValue, boolean strict) {
return unimplemented(context, namedValue, strict);
}
@Override
public Object visit(Object context, RegexpLiteralExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, RelationalExpression expr, boolean strict) {
Scope scope = (Scope) context;
Variable result = scope.createTemporaryVariable();
Operand lhsValue = (Operand) expr.getLhs().accept(context, this, strict);
Operand rhsValue = (Operand) expr.getRhs().accept(context, this, strict);
switch (expr.getOp()) {
case "<":
scope.addInstruction(new LT(result, lhsValue, rhsValue));
break;
case ">":
scope.addInstruction(new LT(result, rhsValue, lhsValue));
break;
case "<=":
scope.addInstruction(new LE(result, lhsValue, rhsValue));
break;
case ">=":
scope.addInstruction(new LE(result, rhsValue, lhsValue));
break;
}
return result;
}
@Override
public Object visit(Object context, ReturnStatement statement, boolean strict) {
Scope scope = (Scope) context;
Operand returnValue = (Operand) acceptOrUndefined(context, statement.getExpr(), strict);
scope.addInstruction(new Return(returnValue));
return returnValue;
}
@Override
public Object visit(Object context, StrictEqualityOperatorExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, StringLiteralExpression expr, boolean strict) {
return new StringLiteral(expr.getLiteral());
}
@Override
public Object visit(Object context, SwitchStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, TernaryExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, ThisExpression expr, boolean strict) {
return This.THIS;
}
@Override
public Object visit(Object context, ThrowStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, TryStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
@Override
public Object visit(Object context, TypeOfOpExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, UnaryMinusExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, UnaryPlusExpression expr, boolean strict) {
return unimplemented(context, expr, strict);
}
@Override
public Object visit(Object context, VariableDeclaration expr, boolean strict) {
Scope scope = (Scope) context;
Variable variable = scope.acquireLocalVariable(expr.getIdentifier());
Operand value = (Operand) acceptOrUndefined(context, expr.getExpr(), strict);
scope.addInstruction(new Copy(variable, value));
return value;
}
@Override
public Object visit(Object context, VariableStatement statement, boolean strict) {
for (VariableDeclaration decl: statement.getVariableDeclarations()) {
decl.accept(context, this, strict);
}
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, VoidOperatorExpression expr, boolean strict) {
// 11.4.2 (GetValue must be called even if we never use the value)
expr.getExpr().accept(context, this, strict);
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, WhileStatement statement, boolean strict) {
Scope scope = (Scope) context;
final Label startLabel = scope.getNewLabel();
final Label doneLabel = scope.getNewLabel();
scope.addInstruction(new LabelInstr(startLabel));
// TEST
scope.addInstruction(new BEQ((Operand) statement.getTest().accept(context, this, strict),
BooleanLiteral.FALSE, doneLabel));
// BODY
statement.getBlock().accept(context, this, strict);
scope.addInstruction(new Jump(startLabel));
// END
scope.addInstruction(new LabelInstr(doneLabel));
return Undefined.UNDEFINED;
}
@Override
public Object visit(Object context, WithStatement statement, boolean strict) {
return unimplemented(context, statement, strict);
}
private Object unimplemented(Object context, Object expr, boolean strict) {
throw new RuntimeException("EXPR: '" + expr + "' is unimplemented.");
}
private Object acceptOrUndefined(Object context, Expression expr, boolean strict) {
return expr != null ? expr.accept(context, this, strict) : Undefined.UNDEFINED;
}
private Variable copyAndReturnValue(Scope scope, Operand value) {
Variable variable = scope.createTemporaryVariable();
scope.addInstruction(new Copy(variable, value));
return variable;
}
private Variable getValueInTemporaryVariable(Scope scope, Operand value) {
if (value != null && value instanceof TemporaryVariable) return (Variable) value;
return copyAndReturnValue(scope, value);
}
}