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; } }