package com.drawbridge.jsengine.ast;
import java.util.LinkedList;
import com.drawbridge.jsengine.Scope;
import com.drawbridge.jsengine.SymbolTableEntry;
import com.drawbridge.jsengine.jsobjects.ExecutionException;
import com.drawbridge.jsengine.jsobjects.JSFunction;
import com.drawbridge.jsengine.jsobjects.JSNativeFunction;
import com.drawbridge.jsengine.jsobjects.JSObject;
import com.drawbridge.jsengine.jsobjects.JSReference;
import com.drawbridge.jsengine.jsobjects.JSString;
import com.drawbridge.jsengine.jsobjects.JSType;
import com.drawbridge.jsengine.jsobjects.JSUndefined;
import com.drawbridge.utils.Utils;
import com.drawbridge.vl.blocks.BlockFunctionCall;
import com.drawbridge.vl.blocks.BlockIdentifier;
import com.google.caja.parser.js.Operator;
import com.google.caja.parser.js.SpecialOperation;
public class SpecialOperationEvaluator extends Evaluator
{
Operator mType;
SpecialOperation mSpecialOperation;
int paramStart = 0;
public SpecialOperationEvaluator(Evaluator parent, Scope scope, SpecialOperation specialOperation) {
super(parent, scope, specialOperation.getFilePosition());
mType = specialOperation.getOperator();
mSpecialOperation = specialOperation;
for (int i = 0; i < specialOperation.children().size(); i++)
{
mChildren.add(Evaluator.getEvaluator(this, scope, specialOperation.children().get(i)));
}
switch (mType)
{
case MEMBER_ACCESS:
paramStart = 2;
break;
case FUNCTION_CALL:
paramStart = 1; // or 2 removed component listener Attempted to add
break;
case CONSTRUCTOR:
paramStart = 1;
break;
case SQUARE_BRACKET:
paramStart = 1;
break;
default:
Utils.out.println("Unexpected specialoperation type:" + mType);
}
// Special case for colour function
boolean isColorGetRGB = false;
if (mChildren.size() > 1 && (mChildren.get(0) instanceof SpecialOperationEvaluator))
{
SpecialOperationEvaluator soEvalFirst = (SpecialOperationEvaluator) mChildren.get(0);
if (soEvalFirst.mChildren.size() > 0 && soEvalFirst.mChildren.get(0) instanceof ReferenceEvaluator)
{
ReferenceEvaluator firstRef = (ReferenceEvaluator) soEvalFirst.mChildren.get(0);
ReferenceEvaluator secondRef = (ReferenceEvaluator) soEvalFirst.mChildren.get(1);
if (firstRef.getIdentifier().getValue().equals("Colour") && secondRef.getIdentifier().getValue().equals("getRGB"))
{
isColorGetRGB = true;
}
}
}
// Every child after first 2 is params
for (int i = paramStart; isColorGetRGB && i < specialOperation.children().size(); i++)
{
Evaluator eval = this.mChildren.get(i);
// This code limits Colour.getRGB parameters to 0-255
if (isColorGetRGB && eval instanceof IntegerLiteralEvaluator)
{
IntegerLiteralEvaluator ilEval = (IntegerLiteralEvaluator) eval;
ilEval.setMaximumInt(255);
ilEval.setMinimumInt(0);
}
}
// Special case for loadImage function
boolean isLoadImage = false;
if (mChildren != null && mChildren.size() == 6 && (mChildren.get(0) instanceof ReferenceEvaluator))
{
ReferenceEvaluator rEval = (ReferenceEvaluator) mChildren.get(0);
if (rEval.getIdentifier().getValue().equals("loadImage"))
{
isLoadImage = true;
}
}
for (int i = paramStart; isLoadImage && i < specialOperation.children().size(); i++)
{
Evaluator eval = mChildren.get(i);
// This code limits Colour.getRGB parameters to 0-255
if (isLoadImage && eval instanceof IntegerLiteralEvaluator && (i == 4 || i == 5))
{
IntegerLiteralEvaluator ilEval = (IntegerLiteralEvaluator) eval;
ilEval.setMaximumInt(Integer.MAX_VALUE);
ilEval.setMinimumInt(100);
}
}
// If there are no special cases, add params as normal
for (int i = paramStart; !isColorGetRGB && !isLoadImage && i < specialOperation.children().size(); i++)
{
Evaluator eval = Evaluator.getEvaluator(this, mScope, specialOperation.children().get(i));
}
}
// Need to make sure that params are always re-evaluated on every evaluate
@Override
public JSType evaluate() throws EvaluatorException, ExecutionException
{
switch (mType)
{
case MEMBER_ACCESS:
// Assume two things in member access
// Process the Object
JSType jsType = new JSUndefined();
String varName = "";
if (mChildren.get(0) instanceof StringLiteralEvaluator)
{
jsType = mChildren.get(0).evaluate();
}
else
{
varName = ((JSReference) mChildren.get(0).evaluate()).mValue;
SymbolTableEntry entry = mScope.getVariable(varName);
if (!varName.equals("this"))
{
if (entry == null)
{
throw new EvaluatorException("Variable \"" + varName + "\" could not be found!");
}
else if (entry != null)
{
jsType = entry.getVariable();
}
}
}
// Process the Property
String propertyName = ((JSReference) mChildren.get(1).evaluate()).mValue;
if (varName.equals("this"))
{
// Special case
SymbolTableEntry entry = mScope.getVariable(propertyName);
if (entry == null)
{
throw new EvaluatorException("Variable \"" + propertyName + "\" could not be found!");
}
else
{
return entry.getVariable();
}
}
else if (jsType instanceof JSObject)
{
JSType result = ((JSObject) jsType).getProperty(propertyName);
if (result == null)
{
((JSObject) jsType).addProperty(propertyName, new JSObject());
result = ((JSObject) jsType).getProperty(propertyName);
}
return result;
}
else if (jsType instanceof JSString)
{
return ((JSString) jsType).getProperty(propertyName);
}
else if (jsType instanceof JSUndefined)
{
throw new EvaluatorException("Cannot evaluate undefined: \"" + propertyName);
}
break;
case FUNCTION_CALL:
if (mChildren.size() >= 1)
{
JSType[] params = evaluateParams();
JSType result = mChildren.get(0).evaluate();
if (result instanceof JSReference)
{
String objectName = ((JSReference) mChildren.get(0).evaluate()).mValue;
SymbolTableEntry entry = mScope.getVariable(objectName);
if (entry != null)
{
result = entry.getVariable();
}
else
throw new EvaluatorException("Could not retrieve function: \"" + objectName + "\"");
}
if (result instanceof JSFunction || result instanceof JSNativeFunction)
{
return ((JSFunction) result).executeFunction(this, params);
}
else
{
throw new ExecutionException("Expected function, got: " + ((result != null) ? result.getClass().getName() : "NULL") + " for:" + mSpecialOperation.toStringDeep());
}
}
else
{
}
break;
case CONSTRUCTOR:
// First thing will always be a reference to the object
String objectName = ((JSReference) mChildren.get(0).evaluate()).mValue;
evaluateParams();
SymbolTableEntry entry = mScope.getVariable(objectName);
JSObject thing = (JSObject) entry.getVariable();
JSType newObject = thing.getProperty("constructor");
return newObject;
case SQUARE_BRACKET:
break;
default:
throw new ExecutionException("Unexpected specialoperation type:" + mType);
}
return new JSUndefined();
}
private JSType[] evaluateParams() throws ExecutionException
{
JSType[] results = new JSType[mChildren.size() - paramStart];
for (int i = paramStart; i < mChildren.size(); i++)
{
try
{
results[i - paramStart] = mChildren.get(i).evaluate();
if (results[i - paramStart] instanceof JSReference)
{
// Utils.out.println("***NAME:" +
// ((JSReference)results[i]).mValue + " " +
// results[i].getClass().getSimpleName());
SymbolTableEntry entry = mScope.getVariable(((JSReference) results[i - paramStart]).mValue);
results[i - paramStart] = (entry != null) ? entry.getVariable() : null;
}
else
{
// Utils.out.println("result " + i + ":" +
// results[i].getClass().getSimpleName());
}
} catch (EvaluatorException e)
{
e.printStackTrace();
}
}
return results;
}
@Override
public LinkedList<com.drawbridge.vl.blocks.Block> getBlocks()
{
LinkedList<com.drawbridge.vl.blocks.Block> results = new LinkedList<com.drawbridge.vl.blocks.Block>();
switch (mType)
{
case MEMBER_ACCESS:
for (Evaluator eval : mChildren)
{
if (eval instanceof ReferenceEvaluator)
{
ReferenceEvaluator refEval = (ReferenceEvaluator) eval;
String name = refEval.getIdentifier().getValue();
results.add(new BlockIdentifier(getFilePosition(), this, name));
}
}
break;
case FUNCTION_CALL:
if (mChildren.size() > 1 && mChildren.get(0) != null)
{
if (mChildren.get(0) instanceof SpecialOperationEvaluator && ((SpecialOperationEvaluator) mChildren.get(0)).mType == Operator.MEMBER_ACCESS)
{
// Assume that member access returns two identifier blocks
LinkedList<com.drawbridge.vl.blocks.Block> maBlocks = mChildren.get(0).getBlocks();
assert (maBlocks.size() == 2);
String ma1 = ((BlockIdentifier) maBlocks.get(0)).getText();
String ma2 = ((BlockIdentifier) maBlocks.get(1)).getText();
results.add(new BlockIdentifier(getFilePosition(), this, ma1)); // Used
// to
// be
// FuncionCallBlock
results.add(new BlockFunctionCall(getFilePosition(), this, ma2));
}
else if (mChildren.get(0) instanceof ReferenceEvaluator)
{
LinkedList<com.drawbridge.vl.blocks.Block> referenceBlocks = mChildren.get(0).getBlocks();
String referenceString = ((BlockIdentifier) referenceBlocks.get(0)).getText();
results.add(new BlockFunctionCall(getFilePosition(), this, referenceString));
}
}
else if (mChildren.size() == 1 && mChildren.get(0) instanceof ReferenceEvaluator)
{
LinkedList<com.drawbridge.vl.blocks.Block> referenceBlocks = mChildren.get(0).getBlocks();
String referenceString = ((BlockIdentifier) referenceBlocks.get(0)).getText();
results.add(new BlockFunctionCall(getFilePosition(), this, referenceString));
}
// Then get the Param Blocks
for (int i = paramStart; i < mChildren.size(); i++)
{
if (mChildren.get(i).getBlocks() != null)
results.addAll(mChildren.get(i).getBlocks());
}
break;
case CONSTRUCTOR:
break;
case SQUARE_BRACKET:
break;
default:
break;
}
return results;
}
}