@Override public boolean visit(AstNode node) { if (node instanceof PropertyGet){ PropertyGet pg = (PropertyGet)node; AstNode target = pg.getTarget(); String propName = pg.getProperty().getIdentifier(); if (target instanceof KeywordLiteral && ConstraintGenUtil.isThis(target)){ if (node.getParent() instanceof Assignment){ Assignment a = (Assignment)node.getParent(); if (a.getLeft() == node){ writtenProperties.add(propName); } else { readProperties.add(propName); } } else { readProperties.add(propName); } readProperties.removeAll(writtenProperties); // if something is read and written, it should only be in the written set } } else if (node instanceof FunctionNode) { // don't recurse into nested function body return false; } return true; }
/** * generate constraints for assignments */ private void processAssignment(Assignment a) throws Error { AstNode left = a.getLeft(); AstNode right = a.getRight(); ITypeTerm expTerm = findOrCreateExpressionTerm(a); if (left instanceof Name){ processAssignToVariable(a, left, right, expTerm); } else if (left instanceof PropertyGet) { PropertyGet pg = (PropertyGet)left; if (pg.getProperty().getIdentifier().equals("prototype")){ processAssignToPrototype(a, left, right, expTerm); } else { processAssignToProperty(a, left, right, expTerm); } processExpression(pg.getTarget()); // TEST } else if (left instanceof ElementGet){ processIndexedAssignment(a, left, right, expTerm); } else { error("unsupported LHS type in Assignment: " + left.getClass().getName(), left); } }
public static TypeErrorMessage badPropertyWrite(PropertyGet node, Type baseType, Type fieldType, Type expType) { SourceLocation location = locationOf(node); String fieldName = node.getProperty().getIdentifier(); if (fieldType != null) { if (baseType instanceof PrimitiveType) { return genericTypeError("cannot assign to property '" + fieldName + "' on primitive type " + describeType(baseType), location); } if (expType instanceof CodeType && fieldType instanceof DefaultType) { return genericTypeError("cannot attach method to field '" + fieldName + "'", location) .withNote("methods may only be attached to object literals, 'this', and constructor prototypes"); } assert !Types.isSubtype(fieldType, expType) || isReadOnly(baseType, fieldName) : "I don't know how to explain this failure"; if (!Types.isSubtype(fieldType, expType)) { return subtypeError("bad assignment to property '" + fieldName + "'", expType, fieldType, location); } else { return genericTypeError("cannot assign to read-only field '" + fieldName + "'", location); } } else { return genericTypeError(shortSrc(node) + " has no property '" + fieldName + "'", location) .withNote("type is " + describeType(baseType)); } }
private void visitAssignmentAtRoot(final Assignment node) { if (node.getRight() instanceof FunctionNode || ingoreAbstractMethod(node)) { if (node.getParent() instanceof ExpressionStatement) { try { visitMethodOrClass(((PropertyGet) node.getLeft()).toSource(), node.getJsDoc()); if (node.getRight() instanceof PropertyGet) { LOG.info("left:{}, right:{}", ((PropertyGet) node.getLeft()).toSource(), ((PropertyGet) node.getRight()).toSource()); } } catch (final ClassCastException e) { LOG.error("Node different then expected in file:{},", fileName, e); } } else { // LOG.debug("Node at linenr {} ignored in file:{}", // node.getLineno(), fileName); } } else if (node.getParent() instanceof ExpressionStatement) { visitMethodOrEnum(((PropertyGet) node.getLeft()).toSource(), node.getJsDoc(), node.getRight()); } }
private void visitOtherNode(final AstNode node) { if (node instanceof PropertyGet && node.getParent() instanceof ExpressionStatement) { if (node.getJsDoc() == null) { LOG.error("Node {} has empty comment in file:{}", node.toSource(), fileName); return; } final JsElement element = parser.parse(fileName, node.getJsDoc()); final String typedef = node.toSource(); if (isMethod(typedef, element)) { addMethodOrField(typedef, element, false); } else if (element.getType() == null) { final JsFile jFile = parseClassOrInterfaceName(typedef, element.isInterface(), element); files.put(typedef, jFile); } else { LOG.error("Type '{}' ignored in file:{}", typedef, fileName); } } }
private boolean tryJavaInvocation(FunctionCall node) throws IOException { if (!(node.getTarget() instanceof PropertyGet)) { return false; } PropertyGet propertyGet = (PropertyGet) node.getTarget(); String callMethod = getJavaMethod(propertyGet.getTarget()); if (callMethod == null || !propertyGet.getProperty().getIdentifier().equals("invoke")) { return false; } MethodReference method = MethodReference.parseIfPossible(callMethod); if (method == null) { return false; } writer.appendMethodBody(method).append('('); printList(node.getArguments()); writer.append(')'); return true; }
@Override public String getFunctionNameLookup(FunctionCall call, SourceCompletionProvider provider) { if (call != null) { StringBuilder sb = new StringBuilder(); if (call.getTarget() instanceof PropertyGet) { PropertyGet get = (PropertyGet) call.getTarget(); sb.append(get.getProperty().getIdentifier()); } sb.append("("); int count = call.getArguments().size(); for (int i = 0; i < count; i++) { sb.append("p"); if (i < count - 1) { sb.append(","); } } sb.append(")"); return sb.toString(); } return null; }
private Node transformPropertyGet(PropertyGet node) { Node target = transform(node.getTarget()); String name = node.getProperty().getIdentifier(); decompiler.addToken(Token.DOT); decompiler.addName(name); return createPropertyGet(target, null, name, 0); }
void decompile(AstNode node) { switch (node.getType()) { case Token.ARRAYLIT: decompileArrayLiteral((ArrayLiteral)node); break; case Token.OBJECTLIT: decompileObjectLiteral((ObjectLiteral)node); break; case Token.STRING: decompiler.addString(((StringLiteral)node).getValue()); break; case Token.NAME: decompiler.addName(((Name)node).getIdentifier()); break; case Token.NUMBER: decompiler.addNumber(((NumberLiteral)node).getNumber()); break; case Token.GETPROP: decompilePropertyGet((PropertyGet)node); break; case Token.EMPTY: break; case Token.GETELEM: decompileElementGet((ElementGet) node); break; case Token.THIS: decompiler.addToken(node.getType()); break; default: Kit.codeBug("unexpected token: " + Token.typeToName(node.getType())); } }
private void checkCallRequiresActivation(AstNode pn) { if ((pn.getType() == Token.NAME && "eval".equals(((Name)pn).getIdentifier())) || (pn.getType() == Token.GETPROP && "eval".equals(((PropertyGet)pn).getProperty().getIdentifier()))) setRequiresActivation(); }
/** * Find or create a term representing a property access */ public PropertyAccessTerm findOrCreatePropertyAccessTerm(ITypeTerm base, String property, PropertyGet pgNode){ if (!propertyAccessTerms.containsKey(base)){ propertyAccessTerms.put(base, new LinkedHashMap<String,PropertyAccessTerm>()); } Map<String,PropertyAccessTerm> map = propertyAccessTerms.get(base); if (!map.containsKey(property)){ map.put(property, new PropertyAccessTerm(base, property, pgNode)); } return map.get(property); }
/** * is right a valid expression to assign into the prototype field of a * constructor? currently we allow object literals, constructor calls, and * expressions of the form B.prototype * * @param right * @return */ private boolean validRHSForAssignToPrototype(AstNode right) { if (right instanceof ObjectLiteral || right instanceof NewExpression) { return true; } if (right instanceof PropertyGet) { PropertyGet pg = (PropertyGet) right; if (pg.getProperty().getIdentifier().equals("prototype")) { return true; } } return false; }
/** * assignment to an object property */ private void processAssignToProperty(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) throws Error { PropertyGet pg = (PropertyGet)left; AstNode base = pg.getTarget(); Name prop = pg.getProperty(); ITypeTerm pgTerm = findOrCreateExpressionTerm(pg); ITypeTerm baseTerm; if (base instanceof KeywordLiteral && ConstraintGenUtil.isThis(base)){ baseTerm = findOrCreateThisTerm(base); } else { baseTerm = generateReceiverConstraints(pg); } int assignLineNo = a.getLineno(); // detect assignments of the form C.prototype.foo = ... if (base instanceof PropertyGet) { PropertyGet basePG = (PropertyGet) base; String baseProp = basePG.getProperty().getIdentifier(); if (baseProp.equals("prototype")) { checkForValidProtoPropAssign(a, pg, assignLineNo, basePG); } } ITypeTerm leftTerm = findOrCreatePropertyAccessTerm(baseTerm, prop.getIdentifier(), null); ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(pgTerm, leftTerm, assignLineNo, (solution) -> typeEqualityError("incompatible types", solution.typeOfTerm(leftTerm), solution.typeOfTerm(pgTerm), locationOf(pg))); addTypeEqualityConstraint(expTerm, leftTerm, assignLineNo, (solution) -> typeEqualityError("incompatible types", solution.typeOfTerm(leftTerm), solution.typeOfTerm(expTerm), locationOf(a))); processCopy(right, rightTerm, leftTerm, assignLineNo, (solution) -> badPropertyWrite(pg, solution.typeOfTerm(baseTerm), hasType(leftTerm, solution) ? solution.typeOfTerm(leftTerm) : null, solution.typeOfTerm(rightTerm))); }
/** * Generate constraints for the (possibly nested) receiver of a property-get expression. */ private ITypeTerm generateReceiverConstraints(PropertyGet pg) throws Error { AstNode base = pg.getTarget(); ITypeTerm baseTerm = findOrCreateExpressionTerm(base); // make term for expression if (base instanceof Name) { Name name = (Name)base; ITypeTerm nameTerm = findOrCreateNameDeclarationTerm(name); // find unique representative for referenced Name addTypeEqualityConstraint(baseTerm, nameTerm, base.getLineno(), null); } else if (base instanceof PropertyGet) { PropertyGet basePG = (PropertyGet)base; ITypeTerm bbaseTerm = generateReceiverConstraints(basePG); String baseProperty = basePG.getProperty().getIdentifier(); ITypeTerm basePATerm; if (basePG.getProperty().getIdentifier().equals("prototype")){ basePATerm = findOrCreateProtoTerm(bbaseTerm, pg.getLineno()); } else { basePATerm = findOrCreatePropertyAccessTerm(bbaseTerm, baseProperty, basePG); } addTypeEqualityConstraint(baseTerm, basePATerm, basePG.getLineno(), null); } else if (base instanceof KeywordLiteral && ConstraintGenUtil.isThis(base)){ processExpression(base); //error("unsupported property get with base this: "+pg.toSource()); } else if (base instanceof ElementGet) { ElementGet baseEGet = (ElementGet) base; processElementGet(baseEGet); } else { System.err.println("base = " + base.toSource() + ", type = " + base.getClass().getName() ); error("unsupported property get: " + pg.toSource(), pg); } return baseTerm; }
/** * Find the textual name of the given node. */ @Nullable private String nameOf(final AstNode node) { if (node instanceof Name) { return ((Name) node).getIdentifier(); } else if (node instanceof PropertyGet) { PropertyGet prop = (PropertyGet) node; return String.format("%s.%s", nameOf(prop.getTarget()), nameOf(prop.getProperty())); } else if (node instanceof StringLiteral) { return ((StringLiteral) node).getValue(); } return null; }
@Override public boolean visit(AstNode node) { switch (node.getType()) { case Token.GETPROP: // foo.bar (firstChild="foo", lastChild="bar") PropertyGet propertyGet = (PropertyGet)node; int propType = propertyGet.getProperty().getType(); if(propertyGet.getTarget().getType()==Token.NAME && (propType == Token.STRING || propType == Token.NAME) && objectName.equals(safeGetString(propertyGet.getTarget())) ){ resultSoFar.add(safeGetString(propertyGet.getProperty())); } break; case Token.GETELEM: // foo["bar"] ElementGet elemGet = (ElementGet)node; int elemType = elemGet.getElement().getType(); if(elemGet.getTarget().getType()==Token.NAME && (elemType == Token.STRING || elemType==Token.NAME) && objectName.equals(safeGetString(elemGet.getTarget())) ){ resultSoFar.add(safeGetString(elemGet.getElement())); } break; default: break; } return true; }
private boolean isJavaMethodRepository(AstNode node) { if (!(node instanceof PropertyGet)) { return false; } PropertyGet propertyGet = (PropertyGet) node; if (!(propertyGet.getLeft() instanceof Name)) { return false; } if (!((Name) propertyGet.getTarget()).getIdentifier().equals("javaMethods")) { return false; } return propertyGet.getProperty().getIdentifier().equals("get"); }
private void print(PropertyGet node) throws IOException { print(node.getLeft(), PRECEDENCE_MEMBER); writer.append('.'); Map<String, NameEmitter> oldNameMap = nameMap; nameMap = Collections.emptyMap(); print(node.getRight()); nameMap = oldNameMap; }
private void getChainedPropertyGetNodesImpl(PropertyGet pg, List<AstNode> nodes){ if (pg.getLeft() instanceof PropertyGet) { getChainedPropertyGetNodesImpl((PropertyGet)pg.getLeft(), nodes); } else { nodes.add(pg.getLeft()); } nodes.add(pg.getRight()); }
@Override public String getFunctionNameLookup(FunctionCall call, SourceCompletionProvider provider) { if (call != null) { StringBuilder sb = new StringBuilder(); if (call.getTarget() instanceof PropertyGet) { PropertyGet get = (PropertyGet) call.getTarget(); sb.append(get.getProperty().getIdentifier()); } sb.append("("); int count = call.getArguments().size(); for (int i = 0; i < count; i++) { AstNode paramNode = call.getArguments().get(i); JavaScriptResolver resolver = provider.getJavaScriptEngine().getJavaScriptResolver(provider); Logger.log("PARAM: " + JavaScriptHelper.convertNodeToSource(paramNode)); try { TypeDeclaration type = resolver.resolveParamNode(JavaScriptHelper.convertNodeToSource(paramNode)); String resolved = type != null ? type.getQualifiedName() : "any"; sb.append(resolved); if (i < count - 1) { sb.append(","); } } catch(IOException io){io.printStackTrace();} } sb.append(")"); return sb.toString(); } return null; }
void decompilePropertyGet(PropertyGet node) { decompile(node.getTarget()); decompiler.addToken(Token.DOT); decompile(node.getProperty()); }
protected Node simpleAssignment(Node left, Node right) { int nodeType = left.getType(); switch (nodeType) { case Token.NAME: String name = ((Name) left).getIdentifier(); if (inUseStrictDirective && ("eval".equals(name) || "arguments".equals(name))) { reportError("msg.bad.id.strict", name); } left.setType(Token.BINDNAME); return new Node(Token.SETNAME, left, right); case Token.GETPROP: case Token.GETELEM: { Node obj, id; // If it's a PropertyGet or ElementGet, we're in the parse pass. // We could alternately have PropertyGet and ElementGet // override getFirstChild/getLastChild and return the appropriate // field, but that seems just as ugly as this casting. if (left instanceof PropertyGet) { obj = ((PropertyGet)left).getTarget(); id = ((PropertyGet)left).getProperty(); } else if (left instanceof ElementGet) { obj = ((ElementGet)left).getTarget(); id = ((ElementGet)left).getElement(); } else { // This branch is called during IRFactory transform pass. obj = left.getFirstChild(); id = left.getLastChild(); } int type; if (nodeType == Token.GETPROP) { type = Token.SETPROP; // TODO(stevey) - see https://bugzilla.mozilla.org/show_bug.cgi?id=492036 // The new AST code generates NAME tokens for GETPROP ids where the old parser // generated STRING nodes. If we don't set the type to STRING below, this will // cause java.lang.VerifyError in codegen for code like // "var obj={p:3};[obj.p]=[9];" id.setType(Token.STRING); } else { type = Token.SETELEM; } return new Node(type, obj, id, right); } case Token.GET_REF: { Node ref = left.getFirstChild(); checkMutableReference(ref); return new Node(Token.SET_REF, ref, right); } } throw codeBug(); }
public PropertyAccessTerm(ITypeTerm base, String property, PropertyGet pgNode){ super(pgNode); this.base = base; this.property = property; }
/** * For a {@link FunctionNode} representing a constructor, if the constructor C is * followed by a sequence of assignments of the form C.prototype.a = ...;, return * a set of all the properties written on the prototype. If the assignments do not * fit that form, return the empty set. * @param consNode * @return */ public static Set<String> getWrittenPrototypeProps(FunctionNode consNode) { Set<String> result = HashSetFactory.make(); AstNode parent = consNode.getParent(); boolean found = false; for (Node child: parent) { if (child instanceof EmptyStatement) { continue; } if (child.equals(consNode)) { found = true; } else if (found) { // looking for a statement of the form C.prototype.a = ...; boolean foundAssign = false; if (child instanceof ExpressionStatement) { AstNode expression = ((ExpressionStatement)child).getExpression(); if (expression instanceof Assignment) { Assignment assign = (Assignment) expression; AstNode lhs = assign.getLeft(); if (lhs instanceof PropertyGet) { PropertyGet pg = (PropertyGet) lhs; AstNode pgTarget = pg.getTarget(); if (pgTarget instanceof PropertyGet) { PropertyGet basePG = (PropertyGet) pgTarget; if (basePG.getProperty().getIdentifier().equals("prototype")) { // BINGO result.add(pg.getProperty().getIdentifier()); foundAssign = true; } } } } } if (!foundAssign) { // stop looking for more assignments break; } } } return result; }
/** * assignment to the "prototype" property */ private void processAssignToPrototype(Assignment a, AstNode left, AstNode right, ITypeTerm expTerm) throws Error { PropertyGet pg = (PropertyGet)left; AstNode base = pg.getTarget(); ITypeTerm pgTerm = findOrCreateExpressionTerm(pg); if (base instanceof Name){ Name name = (Name)base; if (!validRHSForAssignToPrototype(right)) { error( "expression " + right.toSource() + " cannot be assigned to a constructor prototype (line " + right.getLineno() + ")", a); } // can only write to prototype immediately after declaration of // constructor of the same name AstNode parent = a.getParent(); if (!(parent instanceof ExpressionStatement)) { error( "assignment to prototype property not allowed here (line " + a.getLineno() + ")", a); return; } Node prev = getPredecessorNode(parent); if (!(prev instanceof FunctionNode)) { error( "assignment to prototype property only allowed after constructor declaration (line " + a.getLineno() + ")", a); return; } FunctionNode fn = (FunctionNode) prev; String functionName = fn.getName(); String identifier = name.getIdentifier(); if (!functionName.equals(identifier)) { error( "can only assign to prototype of function " + functionName + " here (line " + a.getLineno() + ")", a); return; } ITypeTerm baseTerm = findOrCreateExpressionTerm(base); // make term for expression ITypeTerm nameTerm = findOrCreateNameDeclarationTerm(name); // find unique representative for referenced Name addTypeEqualityConstraint(baseTerm, nameTerm, a.getLineno(), null); // equate them ITypeTerm protoTerm = findOrCreateProtoTerm(baseTerm, pg.getLineno()); ITypeTerm rightTerm = processExpression(right); addTypeEqualityConstraint(pgTerm, protoTerm, a.getLineno(), null); addTypeEqualityConstraint(rightTerm, protoTerm, a.getLineno(), null); addTypeEqualityConstraint(expTerm, protoTerm, a.getLineno(), null); } else { error("processAssignToPrototype: unsupported case for receiver expression: " + base.getClass().getName(), base); } }
private void checkForValidProtoPropAssign(Assignment a, PropertyGet pg, int assignLineNo, PropertyGet basePG) { AstNode baseTarget = basePG.getTarget(); if (!(baseTarget instanceof Name)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Name baseName = (Name) baseTarget; AstNode parent = a.getParent(); if (!(parent instanceof ExpressionStatement)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Node prev = getPredecessorNode(parent); if (prev instanceof FunctionNode) { FunctionNode fn = (FunctionNode) prev; String functionName = fn.getName(); String identifier = baseName.getIdentifier(); if (!functionName.equals(identifier)) { error("can only assign to prototype of function " + functionName + " here (line " + assignLineNo + ")", a); return; } } else if (prev instanceof ExpressionStatement) { // it needs to be an assignment either to C.prototype or C.prototype.foo // TODO clean up this gross code AstNode expression = ((ExpressionStatement)prev).getExpression(); if (!(expression instanceof Assignment)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } Assignment prevAssign = (Assignment) expression; AstNode prevLeft = prevAssign.getLeft(); if (!(prevLeft instanceof PropertyGet)) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } PropertyGet prevPG = (PropertyGet) prevLeft; AstNode prevPGTarget = prevPG.getTarget(); if (prevPG.getProperty().getIdentifier().equals("prototype")) { checkForSameName(assignLineNo, baseName, prevPGTarget); } else if (prevPGTarget instanceof PropertyGet) { PropertyGet prevPGBasePG = (PropertyGet) prevPGTarget; if (!prevPGBasePG.getProperty().getIdentifier().equals("prototype")) { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } checkForSameName(assignLineNo, baseName, prevPGBasePG.getTarget()); } else { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } } else { error("assignment to property of prototype not valid (line " + assignLineNo + ")", a); return; } }
private ITypeTerm findOrCreatePropertyAccessTerm(ITypeTerm term, String name, PropertyGet pgNode){ PropertyAccessTerm t = factory.findOrCreatePropertyAccessTerm(term, name, pgNode); generator.addTermLineNumber(t, pgNode != null ? pgNode.getLineno() : -1); return t; }
private void visitAssignment(final Assignment node) { if (node.getLeft() instanceof PropertyGet) { visitAssignmentAtRoot(node); } }
private boolean ingoreAbstractMethod(final Assignment node) { return node.getRight() instanceof PropertyGet && !"goog.abstractMethod".equals( ((PropertyGet) node.getRight()).toSource()); }
private boolean visit(FunctionCall call) { if (!(call.getTarget() instanceof PropertyGet)) { return true; } PropertyGet propertyGet = (PropertyGet) call.getTarget(); MethodReference methodRef = getJavaMethodSelector(propertyGet.getTarget()); if (methodRef == null || !propertyGet.getProperty().getIdentifier().equals("invoke")) { return true; } for (AstNode arg : call.getArguments()) { arg.visit(this); } MethodReader method = classSource.resolve(methodRef); if (method == null) { diagnostics.error(location, "Java method not found: {{m0}}", methodRef); return false; } int requiredParams = methodRef.parameterCount(); if (!method.hasModifier(ElementModifier.STATIC)) { ++requiredParams; } if (call.getArguments().size() != requiredParams) { diagnostics.error(location, "Invalid number of arguments for method {{m0}}. Expected: " + requiredParams + ", encountered: " + call.getArguments().size(), methodRef); } MethodReference caller = createCallbackMethod(method); MethodReference delegate = repository.methodMap.get(location.getMethod()); repository.callbackCallees.put(caller, methodRef); repository.callbackMethods.computeIfAbsent(delegate, key -> new HashSet<>()).add(caller); validateSignature(method); StringLiteral newTarget = new StringLiteral(); newTarget.setValue("$$JSO$$_" + caller); propertyGet.setTarget(newTarget); return false; }
private List<AstNode> getChainedPropertyGetNodes(PropertyGet pg) { List<AstNode> nodes = new ArrayList<AstNode>(); getChainedPropertyGetNodesImpl(pg, nodes); return nodes; }
public static final boolean isPrototypePropertyGet(PropertyGet pg) { return pg!=null && pg.getLeft() instanceof Name && isPrototypeNameNode(pg.getRight()); }
/** * Returns whether a <code>PropertyGet</code> is a simple one, referencing * an object's value 1 level deep. For example, <code>Object.create</code>. * * @param pg The <code>PropertyGet</code>. * @param expectedObj The expected object value. * @param expectedField The expected string value. * @return Whether the object is what was expected. */ public static final boolean isSimplePropertyGet(PropertyGet pg, String expectedObj, String expectedField) { return pg!=null && isName(pg.getLeft(), expectedObj) && isName(pg.getRight(), expectedField); }