#include "AST.h" #include "CodeContext.h" #include "CtDescriptor.h" #include "CtLocal.h" #include "CtRegistry.h" #include "casts.h" #include "code-grid.h" #include "code.h" #include "compiler.h" #include "ctBox.h" #include "decls.h" #include "errors.h" #include "lgMacro.h" enum LocalityChange { Localize, Globalize, Same }; static bool canCastStatically( const TypeNode &toType, const TypeNode &fromType ) { const Common::Kind fromKind = fromType.kind(); const Common::Kind toKind = toType.kind(); #if 0 /* Downcasting from Object to Arrays is broken because Arrays don't act like Objects. This shortcircuits the code and prevents a dynamic type check from being inserted. WARNING: Very Unsafe, but what the heck. It's only research. */ if (toType.isJavaArrayType() && (fromKind == Common::ClassKind || fromType.decl() == CloneableDecl) && !fromType.isJavaArrayType()) { return true; } #endif switch (toKind) { case Common::ClassKind: switch (fromKind) { case Common::InterfaceKind: return toType.decl() == ObjectDecl; case Common::ClassKind: return isSubClass(fromType.decl(), toType.decl()) || (fromType.isJavaArrayType() && toType.isJavaArrayType() && canCastStatically(*toType.elementType(), *fromType.elementType())); default: break; } case Common::InterfaceKind: switch (fromKind) { case Common::ClassKind: return implements(fromType.decl(), toType.decl()); case Common::InterfaceKind: return isSuperInterface(toType.decl(), fromType.decl()); default: break; } default: break; } return true; } bool DynamicTypeNode::canCastStatically() const { return ::canCastStatically(*dtype(), *opnd0()->type()); } const string DynamicTypeNode::emitTypeCheck( const string &class_info ) { ClassDecl &decl = static_cast< ClassDecl & >(*dtype()->decl()); assert( decl.category() & (Decl::Class | Decl::Interface) ); const string desc = decl.cDescriptorName(); switch (dtype()->kind()) { case ClassKind: return "SUBCLASS_OF(" + class_info + ", " + desc + ')'; case InterfaceKind: return "CLASS_IMPLEMENTS(" + class_info + ", " + desc + ')'; default: cerr << position() << "need dynamic cast handler for kind " << dtype()->kind() << endl; return "1"; } } const string getClassInfo( CodeContext &context, ClassDecl &classDecl, const string &instance, const bool isLocal ) { const CtType &descType = classDecl.cDescriptorType(); const CtType &resultType = *new CtLocal( descType ); const string result = ExprNode::declareTemporary( context, resultType ); // type needed for field address temporary context.depend( ctBox( resultType, isLocal ) ); context << lgMacro( "CLASS_INFO", isLocal ) << "(" << result << ", " << descType << ", " << instance << ");" << endCline; return result; } const string CastNode::emitExpression( CodeContext &context ) { extern const string callNoArgConstructor(TypeNode *t); TypeNode &fromType = *opnd0()->type(); TypeNode &toType = *dtype(); const CtType &resultType = toType.cType(); /* If the C type is identical, that's good enough. */ if (resultType == fromType.cType()) return opnd0()->emitExpression(context); context << "/* Cast "; if (fromType.isNullType()) context << "null"; else context << fromType.cType(); context << " to " << resultType << " */" << endCline; /* convert "null" to a Titanium array */ if (fromType.isNullType() && toType.isTitaniumArrayType()) return callNoArgConstructor(&toType); string specimen = opnd0()->emitExpression( context ); /* Are we changing between local and global? */ const bool fromLocal = fromType.hasReference() && fromType.isLocal(); const bool toLocal = toType.hasReference() && toType.isLocal(); LocalityChange localityChange = fromLocal ? toLocal ? Same : Globalize /* */ : toLocal ? Localize : Same; // Domain/RectDomain casts must be handled specially because RD's are not reference types if (toType.isDomainType() && fromType.isRectDomainType()) { // cast a RectDomain to a Domain assert(!fromLocal); string result = NEW_DOMAIN_CAST_RECTDOMAIN(+, int2string(toType.tiArity())) + '(' + specimen + ')'; if (!toLocal) result = toType.withModifiers(Local)->emitGlobalize( context, "MYBOX", result ); return result; } else if (toType.isRectDomainType() && fromType.isDomainType()) { // cast a Domain to a RectDomain assert(!toLocal); if (fromLocal) specimen = fromType.emitGlobalize( context, "MYBOX", specimen ); return (string) NEW_RECTDOMAIN_DOMAIN_START(+, int2string(toType.tiArity())) + specimen + NEW_RECTDOMAIN_DOMAIN_END(); } // Handle qualification changes and extract dynamic type. // When possible, extract dynamic type locally for speed. string qualified, info_specimen; bool info_local = true; switch (localityChange) { case Globalize: { qualified = fromType.emitGlobalize( context, "MYBOX", specimen ); info_specimen = specimen; break; } case Localize: { qualified = fromType.emitLocalize( context, specimen ); info_specimen = qualified; break; } case Same: qualified = specimen; info_specimen = specimen; info_local = fromLocal; break; } if (fromType.typeIdentNM(&toType)) return qualified; else { const string converted = ((fromType.isPrimitive() || (fromType.isNullType() && localityChange == Same)) ? '(' + resultType + ") " : "*(" + resultType + "*) &") + qualified; if (canCastStatically()) return converted; else { ClassDecl &fromDecl = static_cast< ClassDecl & >( *fromType.decl() ); assert( fromDecl.category() == Decl::Class || fromDecl.category() == Decl::Interface ); const string isNull = info_local ? info_specimen : "!isNull(" + info_specimen + ')'; context << "if (" << isNull << ") {" << endCline; const string result = declareTemporary( context ); if (toType.isJavaArrayType()) { // casting to Java array types handled as a special case context.depend(toType.elementType()->cType()); // PR779 context << "if (" << lgMacro("JAVA_ARRAY_SUBCLASS_OF", fromLocal) << "(" << specimen << ", " << toType.elementType()->cType() << ", " << toType.elementType()->isAtomic() << "))" << endCline; } else { const string class_info = getClassInfo( context, fromDecl, info_specimen, info_local); context << "if (" << emitTypeCheck( class_info ) << ")" << endCline; } context << result << " = " << converted << ";" << endCline << "else" << endCline << " " << MANGLE_TI_LANG_THROWCLASSCASTEXCEPTION << ";" << endCline; context << "} " << endCline; context << "else {" << endCline; context << result << " = " << converted << ";" << endCline; context << "}" << endCline; return result; } } } const string InstanceOfNode::emitExpression( CodeContext &context ) { TypeNode &fromType = *opnd0()->type(); const string specimen = opnd0()->emitExpression( context ); const bool fromLocal = fromType.isLocal(); const bool toLocal = dtype()->isLocal(); const bool mustLocalize = toLocal && !fromLocal; const string accepted = declareTemporary( context ); if (mustLocalize) context << "if (isLocal(" << specimen << "))" << endCline; string ptrval = (fromLocal ? specimen : string("TO_LOCAL(") + specimen + ")"); if (canCastStatically()) { context << accepted << " = " << "(" << ptrval << " != NULL)" << ';' << endCline; } else { ClassDecl &fromDecl = static_cast< ClassDecl & >( *fromType.decl() ); assert( fromDecl.category() == Decl::Class || fromDecl.category() == Decl::Interface); context << "if (" << ptrval << " == NULL) " << accepted << " = 0;" << endCline << "else" << endCline; CodeContext subcontext( context ); if (dtype()->isJavaArrayType()) { // casting to Java array types handled as a special case subcontext << accepted << " = " << lgMacro("JAVA_ARRAY_SUBCLASS_OF", fromLocal) << "(" << specimen << ", " << dtype()->elementType()->cType() << ", " << dtype()->elementType()->isAtomic() << ");" << endCline; } else { const string class_info = getClassInfo( subcontext, fromDecl, specimen, fromLocal); subcontext << accepted << " = " << emitTypeCheck( class_info ) << ';' << endCline; } } if (mustLocalize) context << "else " << accepted << " = 0;" << endCline; return accepted; }