private Node leaveTYPEOF(final UnaryNode unaryNode) { final Expression rhs = unaryNode.getExpression(); final List<Expression> args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { args.add(compilerConstantIdentifier(SCOPE)); args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null } else { args.add(rhs); args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this); end(unaryNode); return runtimeNode; }
private Node leaveTYPEOF(final UnaryNode unaryNode) { final Expression rhs = unaryNode.getExpression(); final List<Expression> args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) { args.add(compilerConstantIdentifier(SCOPE)); args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName())); //null } else { args.add(rhs); args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); end(unaryNode); return runtimeNode; }
/** * Add is a special binary, as it works not only on arithmetic, but for * strings etc as well. */ @Override public Expression leaveADD(final BinaryNode binaryNode) { final Expression lhs = binaryNode.lhs(); final Expression rhs = binaryNode.rhs(); final Type type = binaryNode.getType(); if (type.isObject()) { if (!isAddString(binaryNode)) { return new RuntimeNode(binaryNode, Request.ADD); } } return binaryNode.setLHS(convert(lhs, type)).setRHS(convert(rhs, type)); }
private static Type firstTypeGuessForObject(final Request request) { switch (request) { case ADD: return INT; default: return BOOLEAN; } }
@Override public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { final Request request = runtimeNode.getRequest(); final boolean isEqStrict = request == Request.EQ_STRICT; if(isEqStrict || request == Request.NE_STRICT) { return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), isEqStrict ? Request.IS_UNDEFINED : Request.IS_NOT_UNDEFINED); } return runtimeNode; }
private void loadRuntimeNode(final RuntimeNode runtimeNode) { final List<Expression> args = new ArrayList<>(runtimeNode.getArgs()); if (nullCheck(runtimeNode, args)) { return; } else if(undefinedCheck(runtimeNode, args)) { return; } // Revert a false undefined check to a strict equality check final RuntimeNode newRuntimeNode; final Request request = runtimeNode.getRequest(); if (Request.isUndefinedCheck(request)) { newRuntimeNode = runtimeNode.setRequest(request == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT); } else { newRuntimeNode = runtimeNode; } new OptimisticOperation(newRuntimeNode, TypeBounds.UNBOUNDED) { @Override void loadStack() { for (final Expression arg : args) { loadExpression(arg, TypeBounds.OBJECT); } } @Override void consumeStack() { method.invokestatic( CompilerConstants.className(ScriptRuntime.class), newRuntimeNode.getRequest().toString(), new FunctionSignature( false, false, newRuntimeNode.getType(), args.size()).toString()); } }.emit(); method.convert(newRuntimeNode.getType()); }
private void loadRuntimeNode(final RuntimeNode runtimeNode) { final List<Expression> args = new ArrayList<>(runtimeNode.getArgs()); if (nullCheck(runtimeNode, args)) { return; } else if(undefinedCheck(runtimeNode, args)) { return; } // Revert a false undefined check to a strict equality check final RuntimeNode newRuntimeNode; final Request request = runtimeNode.getRequest(); if (Request.isUndefinedCheck(request)) { newRuntimeNode = runtimeNode.setRequest(request == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT); } else { newRuntimeNode = runtimeNode; } for (final Expression arg : args) { loadExpression(arg, TypeBounds.OBJECT); } method.invokestatic( CompilerConstants.className(ScriptRuntime.class), newRuntimeNode.getRequest().toString(), new FunctionSignature( false, false, newRuntimeNode.getType(), args.size()).toString()); method.convert(newRuntimeNode.getType()); }
private Node leaveTYPEOF(final UnaryNode unaryNode) { final Expression rhs = unaryNode.getExpression(); final List<Expression> args = new ArrayList<>(); if (rhs instanceof IdentNode && !isParamOrVar((IdentNode) rhs)) { args.add(compilerConstantIdentifier(SCOPE)); args.add(LiteralNode.newInstance(rhs, ((IdentNode) rhs).getName())); //null } else { args.add(rhs); args.add(LiteralNode.newInstance(unaryNode)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); return runtimeNode; }
private Node leaveBinaryRuntimeOperator(final BinaryNode binaryNode, final Request request) { try { // Don't do a full RuntimeNode.accept, as we don't want to double-visit the binary node operands return leaveRuntimeNode(new RuntimeNode(binaryNode, request)); } finally { end(binaryNode); } }
private boolean specializationCheck(final RuntimeNode.Request request, final Expression node, final List<Expression> args) { if (!request.canSpecialize()) { return false; } assert args.size() == 2; final Type returnType = node.getType(); load(args.get(0)); load(args.get(1)); Request finalRequest = request; //if the request is a comparison, i.e. one that can be reversed //it keeps its semantic, but make sure that the object comes in //last final Request reverse = Request.reverse(request); if (method.peekType().isObject() && reverse != null) { //rhs is object if (!method.peekType(1).isObject()) { //lhs is not object method.swap(); //prefer object as lhs finalRequest = reverse; } } method.dynamicRuntimeCall( new SpecializedRuntimeNode( finalRequest, new Type[] { method.peekType(1), method.peekType() }, returnType).getInitialName(), returnType, finalRequest); method.convert(node.getType()); method.store(node.getSymbol()); return true; }
/** * Exit a comparison node and do the appropriate replacements. We need to introduce runtime * nodes late for comparisons as types aren't known until the last minute * * Both compares and adds may turn into runtimes node at this level as when we first bump * into the op in Attr, we may type it according to what we know there, which may be wrong later * * e.g. i (int) < 5 -> normal compare * i = object * then the post pass that would add the conversion to the 5 needs to * * @param binaryNode binary node to leave * @param request runtime request * @return lowered cmp node */ @SuppressWarnings("fallthrough") private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { final Expression lhs = binaryNode.lhs(); final Expression rhs = binaryNode.rhs(); Type widest = Type.widest(lhs.getType(), rhs.getType()); boolean newRuntimeNode = false, finalized = false; switch (request) { case EQ_STRICT: case NE_STRICT: if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) { newRuntimeNode = true; widest = Type.OBJECT; finalized = true; } //fallthru default: if (newRuntimeNode || widest.isObject()) { return new RuntimeNode(binaryNode, request).setIsFinal(finalized); } break; } return binaryNode.setLHS(convert(lhs, widest)).setRHS(convert(rhs, widest)); }
Request getRequest() { return request; }
private static Expression createIsUndefined(final Expression parent, final Expression lhs, final Expression rhs, final Request request) { if (isUndefinedIdent(lhs) || isUndefinedIdent(rhs)) { return new RuntimeNode(parent, request, lhs, rhs); } return parent; }