static void genClassIDs(Vec<TypeSymbol*> & typeSymbols) { genComment("Class Type Identification Numbers"); int count=0; forv_Vec(TypeSymbol, ts, typeSymbols) { if (AggregateType* ct = toAggregateType(ts->type)) { if (!isReferenceType(ct) && isClass(ct)) { genGlobalDefClassId(ts->cname, count); count++; } } } }
static QualifiedType returnInfoAsRef(CallExpr* call) { Type* t = call->get(1)->typeInfo(); if (isReferenceType(t)) { return QualifiedType(t, QUAL_REF); } else if (t->symbol->hasFlag(FLAG_WIDE_REF)) { return QualifiedType(t, QUAL_WIDE_REF); } else { if (!t->refType) INT_FATAL(call, "invalid attempt to get reference type"); return QualifiedType(t->refType, QUAL_REF); } }
void cgCallBuiltin(IRLS& env, const IRInstruction* inst) { auto const extra = inst->extra<CallBuiltin>(); auto const callee = extra->callee; auto const returnType = inst->typeParam(); auto const funcReturnType = callee->returnType(); auto const returnByValue = callee->isReturnByValue(); auto const dstData = dstLoc(env, inst, 0).reg(0); auto const dstType = dstLoc(env, inst, 0).reg(1); auto& v = vmain(env); // Whether `t' is passed in/out of C++ as String&/Array&/Object&. auto const isReqPtrRef = [] (MaybeDataType t) { return isStringType(t) || isArrayLikeType(t) || t == KindOfObject || t == KindOfResource; }; if (FixupMap::eagerRecord(callee)) { auto const sp = srcLoc(env, inst, 1).reg(); auto const spOffset = cellsToBytes(extra->spOffset.offset); auto const& marker = inst->marker(); auto const pc = marker.fixupSk().unit()->entry() + marker.fixupBcOff(); auto const synced_sp = v.makeReg(); v << lea{sp[spOffset], synced_sp}; emitEagerSyncPoint(v, pc, rvmtl(), srcLoc(env, inst, 0).reg(), synced_sp); } int returnOffset = rds::kVmMInstrStateOff + offsetof(MInstrState, tvBuiltinReturn); auto args = argGroup(env, inst); if (!returnByValue) { if (isBuiltinByRef(funcReturnType)) { if (isReqPtrRef(funcReturnType)) { returnOffset += TVOFF(m_data); } // Pass the address of tvBuiltinReturn to the native function as the // location where it can construct the return Array, String, Object, or // Variant. args.addr(rvmtl(), returnOffset); args.indirect(); } } // The srcs past the first two (sp and fp) are the arguments to the callee. auto srcNum = uint32_t{2}; // Add the this_ or self_ argument for HNI builtins. if (callee->isMethod()) { if (callee->isStatic()) { args.ssa(srcNum); ++srcNum; } else { // Note that we don't support objects with vtables here (if they may need // a $this pointer adjustment). This should be filtered out during irgen // or before. args.ssa(srcNum); ++srcNum; } } // Add the func_num_args() value if needed. if (callee->attrs() & AttrNumArgs) { // If `numNonDefault' is negative, this is passed as an src. if (extra->numNonDefault >= 0) { args.imm((int64_t)extra->numNonDefault); } else { args.ssa(srcNum); ++srcNum; } } // Add the positional arguments. for (uint32_t i = 0; i < callee->numParams(); ++i, ++srcNum) { auto const& pi = callee->params()[i]; // Non-pointer and NativeArg args are passed by value. String, Array, // Object, and Variant are passed by const&, i.e. a pointer to stack memory // holding the value, so we expect PtrToT types for these. Pointers to // req::ptr types (String, Array, Object) need adjusting to point to // &ptr->m_data. if (TVOFF(m_data) && !pi.nativeArg && isReqPtrRef(pi.builtinType)) { assertx(inst->src(srcNum)->type() <= TPtrToGen); args.addr(srcLoc(env, inst, srcNum).reg(), TVOFF(m_data)); } else if (pi.nativeArg && !pi.builtinType && !callee->byRef(i)) { // This condition indicates a MixedTV (i.e., TypedValue-by-value) arg. args.typedValue(srcNum); } else { args.ssa(srcNum, pi.builtinType == KindOfDouble); } } auto dest = [&] () -> CallDest { if (isBuiltinByRef(funcReturnType)) { if (!returnByValue) return kVoidDest; // indirect return return funcReturnType ? callDest(dstData) // String, Array, or Object : callDest(dstData, dstType); // Variant } return funcReturnType == KindOfDouble ? callDestDbl(env, inst) : callDest(env, inst); }(); cgCallHelper(v, env, CallSpec::direct(callee->nativeFuncPtr()), dest, SyncOptions::Sync, args); // For primitive return types (int, bool, double) and returnByValue, the // return value is already in dstData/dstType. if (returnType.isSimpleType() || returnByValue) return; // For return by reference (String, Object, Array, Variant), the builtin // writes the return value into MInstrState::tvBuiltinReturn, from where it // has to be tested and copied. if (returnType.isReferenceType()) { // The return type is String, Array, or Object; fold nullptr to KindOfNull. assertx(isBuiltinByRef(funcReturnType) && isReqPtrRef(funcReturnType)); v << load{rvmtl()[returnOffset], dstData}; if (dstType.isValid()) { auto const sf = v.makeReg(); auto const rtype = v.cns(returnType.toDataType()); auto const nulltype = v.cns(KindOfNull); v << testq{dstData, dstData, sf}; v << cmovb{CC_Z, sf, rtype, nulltype, dstType}; } return; } if (returnType <= TCell || returnType <= TBoxedCell) { // The return type is Variant; fold KindOfUninit to KindOfNull. assertx(isBuiltinByRef(funcReturnType) && !isReqPtrRef(funcReturnType)); static_assert(KindOfUninit == 0, "KindOfUninit must be 0 for test"); v << load{rvmtl()[returnOffset + TVOFF(m_data)], dstData}; if (dstType.isValid()) { auto const rtype = v.makeReg(); v << loadb{rvmtl()[returnOffset + TVOFF(m_type)], rtype}; auto const sf = v.makeReg(); auto const nulltype = v.cns(KindOfNull); v << testb{rtype, rtype, sf}; v << cmovb{CC_Z, sf, rtype, nulltype, dstType}; } return; } not_reached(); }
/** * \a obj is an expression to a type of an QObject (or pointer to) that is the sender or the receiver * \a method is an expression like SIGNAL(....) or SLOT(....) * * This function try to find the matching signal or slot declaration, and register its use. */ void QtSupport::handleSignalOrSlot(clang::Expr* obj, clang::Expr* method) { if (!obj || !method) return; obj = obj->IgnoreImpCasts(); method = method->IgnoreImpCasts(); auto objType = obj->getType().getTypePtrOrNull(); if (!objType) return; const clang::CXXRecordDecl* objClass = objType->getPointeeCXXRecordDecl(); if (!objClass) { // It can be a non-pointer if called like: foo.connect(....); objClass = objType->getAsCXXRecordDecl(); if (!objClass) return; } const clang::StringLiteral *methodLiteral = clang::dyn_cast<clang::StringLiteral>(method); if (!methodLiteral) { // try qFlagLocation clang::CallExpr *flagLoc = clang::dyn_cast<clang::CallExpr>(method); if (!flagLoc || flagLoc->getNumArgs() != 1 || !flagLoc->getDirectCallee() || flagLoc->getDirectCallee()->getName() != "qFlagLocation") return; methodLiteral = clang::dyn_cast<clang::StringLiteral>(flagLoc->getArg(0)->IgnoreImpCasts()); if (!methodLiteral) return; } if (methodLiteral->getCharByteWidth() != 1) return; auto signature = methodLiteral->getString().trim(); if (signature.size() < 4) return; if (signature.find('\0') != signature.npos) { signature = signature.substr(0, signature.find('\0')).trim(); } auto lParenPos = signature.find('('); auto rParenPos = signature.find(')'); if (rParenPos == std::string::npos || rParenPos < lParenPos || lParenPos < 2) return; llvm::StringRef methodName = signature.slice(1 , lParenPos).trim(); // Try to find the method which match this name in the given class or bases. auto candidates = lookUpCandidates(objClass, methodName); clang::LangOptions lo; lo.CPlusPlus = true; lo.Bool = true; clang::PrintingPolicy policy(lo); policy.SuppressScope = true; auto argPos = lParenPos + 1; unsigned int arg = 0; while (argPos < signature.size() && !candidates.empty()) { // Find next comma to extract the next argument auto searchPos = argPos; while (searchPos < signature.size() && signature[searchPos] != ',' && signature[searchPos] != ')') { if (signature[searchPos] == '<') { int depth = 0; int templDepth = 0; searchPos++; while(searchPos < signature.size() && depth >= 0 && templDepth >= 0) { switch (signature[searchPos]) { case '(': case '[': case '{': depth++; break; case ')': case ']': case '}': depth--; break; case '>': if (depth == 0) templDepth--; break; case '<': if (depth == 0) templDepth++; break; } ++searchPos; } continue; } ++searchPos; } if (searchPos == signature.size()) return; llvm::StringRef argument = signature.substr(argPos, searchPos - argPos).trim(); // Skip the const at the beginning if (argument.startswith("const ") && argument.endswith("&")) argument = argument.slice(6, argument.size()-1).trim(); argPos = searchPos + 1; if (argument.empty() && signature[searchPos] == ')' && arg == 0) break; //No arguments //Now go over the candidates and prune the impossible ones. auto it = candidates.begin(); while (it != candidates.end()) { if ((*it)->getNumParams() < arg + 1) { // Not enough argument it = candidates.erase(it); continue; } auto type = (*it)->getParamDecl(arg)->getType(); // remove const or const & if (type->isReferenceType() && type.getNonReferenceType().isConstQualified()) type = type.getNonReferenceType(); type.removeLocalConst(); auto typeString_ = type.getAsString(policy); auto typeString = llvm::StringRef(typeString_).trim(); // Now compare the two string without mathcin spaces, auto sigIt = argument.begin(); auto parIt = typeString.begin(); while (sigIt != argument.end() && parIt != typeString.end()) { if (*sigIt == *parIt) { ++sigIt; ++parIt; } else if (*sigIt == ' ') { ++sigIt; } else if (*parIt == ' ') { ++parIt; } else if (*sigIt == 'n' && llvm::StringRef(sigIt, 9).startswith("nsigned ")) { // skip unsigned sigIt += 8; } else if (*parIt == 'n' && llvm::StringRef(parIt, 9).startswith("nsigned ")) { // skip unsigned parIt += 8; } else { break; } } if (sigIt != argument.end() || parIt != typeString.end()) { // Did not match. it = candidates.erase(it); continue; } ++it; } arg++; if (signature[searchPos] == ')') break; } if (argPos != signature.size()) return; // Remove candidates that needs more argument candidates.erase(std::remove_if(candidates.begin(), candidates.end(), [=](clang::CXXMethodDecl *it) { return it->getMinRequiredArguments() > arg && !(it->getNumParams() == arg+1 && it->getParamDecl(arg)->getType().getAsString(policy) == "QPrivateSignal"); }), candidates.end()); if (candidates.empty()) return; auto used = candidates.front(); clang::SourceRange range = methodLiteral->getSourceRange(); if (methodLiteral->getNumConcatenated() >= 2) { auto &sm = annotator.getSourceMgr(); // Goes two level up in the macro expension: First level is the # expansion, Second level is SIGNAL macro auto r = sm.getImmediateExpansionRange(methodLiteral->getStrTokenLoc(1)); #if CLANG_VERSION_MAJOR < 7 range = { sm.getImmediateExpansionRange(r.first).first, sm.getImmediateExpansionRange(r.second).second }; #else range = { sm.getImmediateExpansionRange(r.getBegin()).getBegin(), sm.getImmediateExpansionRange(r.getEnd()).getEnd() }; #endif // now remove the SIGNAL or SLOT macro from the range. auto skip = clang::Lexer::MeasureTokenLength(range.getBegin(), sm, annotator.getLangOpts()); range.setBegin(range.getBegin().getLocWithOffset(skip+1)); // remove the ')' while we are on it range.setEnd(range.getEnd().getLocWithOffset(-1)); } annotator.registerUse(used, range, Annotator::Call, currentContext, Annotator::Use_Address); }