wrapSuperCall(bareSuper, superRef, thisRef, body) { let bareSuperNode = bareSuper.node; if (this.isLoose) { bareSuperNode.arguments.unshift(t.thisExpression()); if (bareSuperNode.arguments.length === 2 && t.isSpreadElement(bareSuperNode.arguments[1]) && t.isIdentifier(bareSuperNode.arguments[1].argument, { name: "arguments" })) { // special case single arguments spread bareSuperNode.arguments[1] = bareSuperNode.arguments[1].argument; bareSuperNode.callee = t.memberExpression(superRef, t.identifier("apply")); } else { bareSuperNode.callee = t.memberExpression(superRef, t.identifier("call")); } } else { bareSuperNode = optimiseCall( t.callExpression( t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")), [this.classRef] ), t.thisExpression(), bareSuperNode.arguments ); } let call = t.callExpression( this.file.addHelper("possibleConstructorReturn"), [t.thisExpression(), bareSuperNode] ); let bareSuperAfter = this.bareSuperAfter.map((fn) => fn(thisRef)); if (bareSuper.parentPath.isExpressionStatement() && bareSuper.parentPath.container === body.node.body && body.node.body.length - 1 === bareSuper.parentPath.key) { // this super call is the last statement in the body so we can just straight up // turn it into a return if (this.superThises.length || bareSuperAfter.length) { bareSuper.scope.push({ id: thisRef }); call = t.assignmentExpression("=", thisRef, call); } if (bareSuperAfter.length) { call = t.toSequenceExpression([call, ...bareSuperAfter, thisRef]); } bareSuper.parentPath.replaceWith(t.returnStatement(call)); } else { bareSuper.replaceWithMultiple([ t.variableDeclaration("var", [ t.variableDeclarator(thisRef, call) ]), ...bareSuperAfter, t.expressionStatement(thisRef) ]); } }
function classMethod(path: NodePath, callId: Object) { let node = path.node; let body = node.body; node.async = false; let container = t.functionExpression(null, [], t.blockStatement(body.body), true); container.shadow = true; body.body = [ t.returnStatement(t.callExpression( t.callExpression(callId, [container]), [] )) ]; }
getSuperProperty(property: Object, isComputed: boolean): Object { return t.callExpression( this.file.addHelper("get"), [ t.callExpression( t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")), [ this.isStatic ? this.getObjectRef() : t.memberExpression(this.getObjectRef(), t.identifier("prototype")) ] ), isComputed ? property : t.stringLiteral(property.name), t.thisExpression() ] ); }
function plainFunction(path: NodePath, callId: Object) { let node = path.node; node.async = false; node.generator = true; path.traverse(awaitVisitor); let built = t.callExpression(callId, [node]); let container = buildWrapper({ FUNCTION: built, PARAMS: node.params.map(() => path.scope.generateUidIdentifier("x")) }).expression; let retFunction = container.body.body[1].argument; if (path.isFunctionDeclaration()) { let declar = t.variableDeclaration("let", [ t.variableDeclarator( t.identifier(node.id.name), t.callExpression(container, []) ) ]); declar._blockHoist = true; nameFunction({ node: retFunction, parent: declar.declarations[0], scope: path.scope }); path.replaceWith(declar); } else { nameFunction({ node: retFunction, parent: path.parent, scope: path.scope }); if (retFunction.id || node.params.length) { // we have an inferred function id or params so we need this wrapper path.replaceWith(t.callExpression(container, [])); } else { // we can omit this wrapper as the conditions it protects for do not apply path.replaceWith(built); } } }
run() { let superName = this.superName; let file = this.file; let body = this.body; // let constructorBody = this.constructorBody = t.blockStatement([]); this.constructor = this.buildConstructor(); // let closureParams = []; let closureArgs = []; // if (this.isDerived) { closureArgs.push(superName); superName = this.scope.generateUidIdentifierBasedOnNode(superName); closureParams.push(superName); this.superName = superName; } // this.buildBody(); // make sure this class isn't directly called constructorBody.body.unshift(t.expressionStatement(t.callExpression(file.addHelper("classCallCheck"), [ t.thisExpression(), this.classRef ]))); body = body.concat(this.staticPropBody.map((fn) => fn(this.classRef))); if (this.classId) { // named class with only a constructor if (body.length === 1) return t.toExpression(body[0]); } // body.push(t.returnStatement(this.classRef)); let container = t.functionExpression(null, closureParams, t.blockStatement(body)); container.shadow = true; return t.callExpression(container, closureArgs); }
addTemplateObject( helperName: string, strings: Array<Object>, raw: Object, ): Object { // Generate a unique name based on the string literals so we dedupe // identical strings used in the program. let stringIds = raw.elements.map(function(string) { return string.value; }); let name = `${helperName}_${raw.elements.length}_${stringIds.join(",")}`; let declar = this.declarations[name]; if (declar) return declar; let uid = this.declarations[name] = this.scope.generateUidIdentifier("templateObject"); let helperId = this.addHelper(helperName); let init = t.callExpression(helperId, [strings, raw]); init._compact = true; this.scope.push({ id: uid, init: init, _blockHoist: 1.9 // This ensures that we don't fail if not using function expression helpers }); return uid; }
ast.program.body.forEach((node, index, body) => { if (node === statementPath.node) { body.splice(index + 1, 0, t.expressionStatement(t.callExpression(t.identifier('setStore'), [ t.identifier(storeName) ]))); } });
it("does not update an object property type if the variable has been reassigned in the function call", function () { // arrange const personExpression = t.identifier("person"); const callExpression = t.callExpression(t.identifier("setName"), [personExpression]); const setNameDeclaration = functionDeclaration(t.identifier("setName"), t.identifier("person")); // body is p = { name: 'Test' } const person = new Symbol("person", SymbolFlags.Variable); program.symbolTable.setSymbol(personExpression, person); const personType = ObjectType.create(); context.setType(person, personType); const personParameter = new Symbol("person", SymbolFlags.Variable); program.symbolTable.setSymbol(setNameDeclaration.params[0], personParameter); const setNameType = new FunctionType(TypeVariable.create(), [TypeVariable.create()], VoidType.create(), setNameDeclaration); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(setNameType); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[0]).returns(personType); // a property name is added in the called function to the person object typeInferenceAnalysis.analyse = (node, typeEnvironment) => new Map([[null, typeEnvironment.setType(personParameter, ObjectType.create([[ new Symbol("name"), StringType.create() ]]))]]); // act rule.refine(callExpression, context); // assert expect(context.getType(person)).to.be.instanceOf(ObjectType); expect(context.getType(person).getType(new Symbol("name"))).to.be.undefined; });
it("updates the types of objects when they have been passed as arguments", function () { // arrange const personExpression = t.identifier("person"); const callExpression = t.callExpression(t.identifier("setName"), [personExpression]); const setNameDeclaration = functionDeclaration(t.identifier("setName"), t.identifier("person")); // body is p.name = 'Test' const person = new Symbol("person", SymbolFlags.Variable); program.symbolTable.setSymbol(personExpression, person); const personType = ObjectType.create(); context.setType(person, personType); const personParameter = new Symbol("person", SymbolFlags.Variable); program.symbolTable.setSymbol(setNameDeclaration.params[0], personParameter); const setNameType = new FunctionType(TypeVariable.create(), [TypeVariable.create()], VoidType.create(), setNameDeclaration); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(setNameType); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[0]).returns(personType); // a property name is added in the called function to the person object typeInferenceAnalysis.analyse = (node, typeEnvironment) => new Map([[null, typeEnvironment.substitute(personType, personType.addProperty(new Symbol("name"), StringType.create()))]]); // act rule.refine(callExpression, context); // assert expect(context.getType(person)).to.be.instanceOf(ObjectType); expect(context.getType(person).getType(new Symbol("name"))).to.be.instanceOf(StringType); });
it("returns for a call expression", function () { // arrange const node = t.callExpression(t.identifier("log"), []); // act, assert expect(rule.canRefine(node)).to.be.true; });
it("adds type mappings from the function declaration context to the call context", function () { // arrange const callExpression = t.callExpression(t.identifier("log"), [t.stringLiteral("Hy")]); const logDeclaration = functionDeclaration(t.identifier("log"), t.identifier("m")); const m = new Symbol("m", SymbolFlags.Variable); program.symbolTable.setSymbol(logDeclaration.params[0], m); const x = new Symbol("x", SymbolFlags.Variable); // variable from the declaration scope const logType = new FunctionType(TypeVariable.create(), [TypeVariable.create()], VoidType.create(), logDeclaration); logType.typeEnvironment = TypeEnvironment.EMPTY.setType(x, StringType.create()); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(logType); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[0]).returns(StringType.create()); typeInferenceAnalysis.analyse.withArgs(logDeclaration.body).returns(new Map([[null, TypeEnvironment.EMPTY]])); // act rule.refine(callExpression, context); // assert const analyseCall = typeInferenceAnalysis.analyse.getCall(0); const analyseTypeEnv = analyseCall.args[1]; expect(analyseTypeEnv.getType(x)).to.be.instanceOf(StringType); });
it("sets this to the object of the callee, if the callee is a member expression", function () { // arrange const personNode = t.identifier("person"); const logMember = t.memberExpression(personNode, t.identifier("log")); const callExpression = t.callExpression(logMember, [t.stringLiteral("Hy")]); const logDeclaration = functionDeclaration(t.identifier("log"), t.identifier("m")); const m = new Symbol("m", SymbolFlags.Variable); program.symbolTable.setSymbol(logDeclaration.params[0], m); const log = new Symbol("log", SymbolFlags.Function & SymbolFlags.Property); const personType = ObjectType.create([[log, new FunctionType(new TypeVariable(), [], VoidType.create(), logDeclaration)]]); typeInferenceAnalysis.infer.withArgs(personNode).returns(personType); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(new FunctionType(TypeVariable.create(), [TypeVariable.create()], VoidType.create(), logDeclaration)); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[0]).returns(StringType.create()); typeInferenceAnalysis.analyse.withArgs(logDeclaration.body).returns(new Map([[null, TypeEnvironment.EMPTY]])); sinon.stub(context, "getObjectType").withArgs(logMember).returns(personType); // act rule.refine(callExpression, context); // assert const analyseCall = typeInferenceAnalysis.analyse.getCall(0); const analyseTypeEnv = analyseCall.args[1]; const thiz = logDeclaration.scope.resolveSymbol("this"); expect(analyseTypeEnv.getType(thiz)).to.be.equals(personType); });
export default function bindifyDecorators(decorators: Array<NodePath>): Array<NodePath> { for (let decoratorPath of decorators) { let decorator = decoratorPath.node; let expression = decorator.expression; if (!t.isMemberExpression(expression)) continue; let temp = decoratorPath.scope.maybeGenerateMemoised(expression.object); let ref; let nodes = []; if (temp) { ref = temp; nodes.push(t.assignmentExpression("=", temp, expression.object)); } else { ref = expression.object; } nodes.push(t.callExpression( t.memberExpression( t.memberExpression(ref, expression.property, expression.computed), t.identifier("bind") ), [ref] )); if (nodes.length === 1) { decorator.expression = nodes[0]; } else { decorator.expression = t.sequenceExpression(nodes); } } }
it("analyses the body of a callback that has been passed as argument", function () { // arrange const predicateDeclaration = functionDeclaration(t.identifier("isEven"), t.identifier("x")); // body x % 2 === 0; const predicate = new FunctionType(VoidType.create(), [TypeVariable.create()], TypeVariable.create(), predicateDeclaration); const x = new Symbol("x", SymbolFlags.Variable); program.symbolTable.setSymbol(predicateDeclaration.params[0], x); const elementType = TypeVariable.create(); const predicateExpectedSignature = new FunctionType(VoidType.create(), [elementType], BooleanType.create()); const filter = new FunctionType(VoidType.create(), [ArrayType.of(elementType), predicateExpectedSignature], ArrayType.of(elementType)); const callExpression = t.callExpression(t.identifier("filter"), [t.arrayExpression([t.numericLiteral(1), t.numericLiteral(2)]), t.identifier("isEven")]); const callContext = context.fresh(); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(filter); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[0]).returns(ArrayType.of(NumberType.create())); typeInferenceAnalysis.infer.withArgs(callExpression.arguments[1]).returns(predicate); sinon.stub(context, "fresh").returns(callContext); sinon.stub(context, "unify").returnsArg(0); // return the actual argument type // unifying the parameter with the argument type requires that the type variables in the parameter type are substituted callContext.unify = (t1, t2) => { if (t1 instanceof ArrayType && t1.of instanceof NumberType && t2 instanceof ArrayType && t2.of instanceof TypeVariable) { callContext.substitute(t2.of, t1.of); return t2; } }; // Fake analysis of the predicate function by setting the return type callContext.analyse = () => callContext.typeEnvironment = callContext.typeEnvironment.setType(Symbol.RETURN, BooleanType.create()); // expect expect(rule.refine(callExpression, context)).to.be.instanceOf(ArrayType).and.to.have.property("of").that.is.an.instanceOf(NumberType); });
ast_with: function(ast) { let exclude = this.options.globals ? this.options.globals.concat(INTERNAL_VARIABLES) : INTERNAL_VARIABLES; exclude = exclude.concat(this.runtimeFunctionsUsed.map(function (name) { return 'pug_' + name; })); exclude.push('undefined', 'this', 'locals') let vars = findGlobals(t.program(ast)).map(function(v) { return v.name }).filter(function(v) { return exclude.indexOf(v) === -1 }) if (vars.length > 0) { let bag = 'locals' ast = [t.expressionStatement( t.callExpression( t.memberExpression(t.functionExpression(null, vars.map(function(v) { return t.identifier(v)}), t.blockStatement(ast)), t.identifier('call')), [ t.thisExpression() ].concat(vars.map(function(v) { return t.conditionalExpression( t.binaryExpression('in', t.stringLiteral(v), t.logicalExpression('||', t.identifier(bag), t.objectExpression([]))), t.memberExpression(t.logicalExpression('||', t.identifier(bag), t.objectExpression([])), t.identifier(v)), t.conditionalExpression( t.binaryExpression('!==', t.unaryExpression('typeof', t.identifier(v)), t.stringLiteral('undefined')), t.identifier(v), t.identifier('undefined') ) ) })) ))] } return ast; },
function getRuntimeMarkDecl(blockPath) { let block = blockPath.node; assert.ok(Array.isArray(block.body)); let info = getMarkInfo(block); if (info.decl) { return info.decl; } info.decl = t.variableDeclaration("var", [ t.variableDeclarator( blockPath.scope.generateUidIdentifier("marked"), t.callExpression( t.memberExpression( t.arrayExpression([]), t.identifier("map"), false ), [util.runtimeProperty("mark")] ) ) ]); blockPath.unshiftContainer("body", info.decl); return info.decl; }
it("detects recursive calls with the same arguments and uses the return type of the previously called function", function () { // arrange const func = functionDeclaration(t.identifier("successor"), t.identifier("x")); const x = new Symbol("x", SymbolFlags.Variable); program.symbolTable.setSymbol(func.params[0], x); const funcT = new FunctionType(new TypeVariable(), [], VoidType.create(), func); const call = t.callExpression(t.identifier("successor"), [t.numericLiteral(4)]); typeInferenceAnalysis.infer.withArgs(call.callee).returns(funcT); let analyseCount = 0; typeInferenceAnalysis.analyse = (node, typeEnvironment) => { ++analyseCount; const recursiveCall = t.callExpression(t.identifier("successor"), [t.binaryExpression("-", t.identifier("x"), t.numericLiteral(-1))]); typeInferenceAnalysis.infer.withArgs(recursiveCall.callee).returns(funcT); program.symbolTable.setSymbol(recursiveCall.arguments[0].left, x); rule.refine(recursiveCall, context); return new Map([[null, typeEnvironment.setType(Symbol.RETURN, NumberType.create())]]); }; typeInferenceAnalysis.infer.returns(NumberType.create()); // act expect(rule.refine(call, context)).to.be.instanceOf(NumberType); expect(analyseCount).to.equals(1); });
// add, addCatch and addFinally were designed to be called only one time each // at most. Call them more at your own risk. // // addCatch() and addFinally() are not guaranteed to handle return values // correctly. FIXME. constructor(inner, dirtyAllowed, respName, errName) { this._inner = inner; this._dirtyAllowed = dirtyAllowed; this._respName = respName; this._errName = errName; this._ast = callExpression(memberExpression(identifier('Promise'), identifier('resolve')), []); }
it("throws if the type is not a function type", function () { // arrange const callExpression = t.callExpression(t.identifier("log"), [t.stringLiteral("Hy")]); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(StringType.create()); // act, assert expect(() => rule.refine(callExpression, context)).to.throw("Type inference failure: Cannot invoke the non function type string."); });
export default opts => { const tests = opts.candidates.reduce((list, candidate) => { if (candidate.type === 'keyword') { list.push( createTest(candidate.value), createTest(candidate.value.toUpperCase()), ); } if (candidate.type === 'data') { const camel = camelCase(candidate.value); if (fixtures[camel]) { // eslint-disable-line const values = fixtures[camel].fixtures; // eslint-disable-line list.push.apply(list, values.valid.map(fixture => { return createTest(fixture); })); list.push.apply(list, values.invalid.map(fixture => { return createTest(fixture, false); })); if (candidate.min === 1 && candidate.max === false && candidate.separator === ',') { list.push( createTest(`${values.valid[0]}, ${values.valid[0]}`), createTest(`${values.valid[0]}, ${values.valid[0]},`, false), createTest(`var(--foo), var(--bar)`), createTest(`var(--foo), var(--bar),`, false) ); } } } return list; }, [...globals, ...globals.map(val => val.toUpperCase()), 'var(--foo)'].map(ident => createTest(ident))); if (opts.properties.length === 1) { return generateProgram([ template(`const property = PROPERTY;`)({ PROPERTY: t.stringLiteral(opts.properties[0]), }), template(`export default SUITE;`)({ SUITE: t.arrayExpression(tests), }), ]); } return generateProgram([ template(` export default PROPERTIES.reduce((suite, property) => { SUITE; return suite; }, []); `)({ PROPERTIES: arrayOfStrings(opts.properties), SUITE: t.callExpression( t.memberExpression( t.identifier('suite'), t.identifier('push') ), tests), }), ]); };
it("infers the return type from the function signature", function () { // arrange const trimType = new FunctionType(VoidType.create(), [], StringType.create()); const callExpression = t.callExpression(t.identifier("trim"), []); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(trimType); // act, assert expect(rule.refine(callExpression, context)).to.be.instanceOf(StringType); });
typeInferenceAnalysis.analyse = (node, typeEnvironment) => { ++analyseCount; const recursiveCall = t.callExpression(t.identifier("successor"), [t.binaryExpression("-", t.identifier("x"), t.numericLiteral(-1))]); typeInferenceAnalysis.infer.withArgs(recursiveCall.callee).returns(funcT); program.symbolTable.setSymbol(recursiveCall.arguments[0].left, x); rule.refine(recursiveCall, context); return new Map([[null, typeEnvironment.setType(Symbol.RETURN, NumberType.create())]]); };
it("throws if the call misses required arguments", function () { // arrange const includesType = new FunctionType(VoidType.create(), [StringType.create()], StringType.create()); const callExpression = t.callExpression(t.identifier("includes"), []); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(includesType); // act, assert expect(() => rule.refine(callExpression, context)).to.throw("Type inference failure: The argument 1 with type 'undefined' is not a subtype of the required parameter type 'string'."); });
it("throws if the this type of the called function is not a subtype of the this expected by the function", function () { // arrange const trimType = new FunctionType(StringType.create(), [], StringType.create()); const callExpression = t.callExpression(t.identifier("trim"), []); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(trimType); // act, assert expect(() => rule.refine(callExpression, context)).to.throw("Type inference failure: The function cannot be called with this of type 'undefined' whereas 'string' is required."); });
it("returns any if the called function any", function () { // arrange const callExpression = t.callExpression(t.identifier("log"), [t.stringLiteral("Hy")]); typeInferenceAnalysis.infer.withArgs(callExpression.callee).returns(AnyType.create()); // assert expect(rule.refine(callExpression, context)).to.be.instanceOf(AnyType); });
var formCSSXElement = function (args, pure) { return t.callExpression( t.memberExpression( t.identifier('cssx'), t.identifier('push') ), [t.arrayExpression(args)] ); };
t.switchStatement(t.identifier('variableName'), Object.keys(this.accessors).map(function(identifierName) { let accessOriginalVariable = noRewire(t.identifier(identifierName)); if(this.isWildcardImport[identifierName]) { accessOriginalVariable = t.callExpression(filterWildcardImportIdentifier, [ accessOriginalVariable ]); } return t.switchCase(t.stringLiteral(identifierName), [ t.returnStatement(accessOriginalVariable) ] ); }, this)),
getSuperProperty(property: Object, isComputed: boolean): Object { return t.callExpression( this.file.addHelper("get"), [ getPrototypeOfExpression(this.getObjectRef(), this.isStatic), isComputed ? property : t.stringLiteral(property.name), t.thisExpression() ] ); }
Ep.getDispatchLoop = function() { let self = this; let cases = []; let current; // If we encounter a break, continue, or return statement in a switch // case, we can skip the rest of the statements until the next case. let alreadyEnded = false; self.listing.forEach(function(stmt, i) { if (self.marked.hasOwnProperty(i)) { cases.push(t.switchCase( t.numericLiteral(i), current = [])); alreadyEnded = false; } if (!alreadyEnded) { current.push(stmt); if (t.isCompletionStatement(stmt)) alreadyEnded = true; } }); // Now that we know how many statements there will be in this.listing, // we can finally resolve this.finalLoc.value. this.finalLoc.value = this.listing.length; cases.push( t.switchCase(this.finalLoc, [ // Intentionally fall through to the "end" case... ]), // So that the runtime can jump to the final location without having // to know its offset, we provide the "end" case as a synonym. t.switchCase(t.stringLiteral("end"), [ // This will check/clear both context.thrown and context.rval. t.returnStatement( t.callExpression(this.contextProperty("stop"), []) ) ]) ); return t.whileStatement( t.numericLiteral(1), t.switchStatement( t.assignmentExpression( "=", this.contextProperty("prev"), this.contextProperty("next") ), cases ) ); };
const addHotAccept = (node) => { const acceptCall = t.callExpression( t.memberExpression(t.identifier('module.hot'), t.identifier('accept')), [t.identifier('function (err) {err && console.error(`Can not accept module: `, err)}')] ) const statement = t.ifStatement( t.identifier('(typeof module === "object" && module.hot) && !(typeof global === "object" && global.noCycleHmr)'), t.expressionStatement(acceptCall) ) node.body.unshift(statement); }