/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform.statement; import gw.internal.gosu.ir.nodes.IRProperty; import gw.internal.gosu.ir.nodes.IRPropertyFactory; import gw.internal.gosu.ir.nodes.IRPropertyFromPropertyInfo; import gw.internal.gosu.ir.transform.ExpressionTransformer; import gw.internal.gosu.ir.transform.TopLevelTransformationContext; import gw.internal.gosu.ir.transform.util.AccessibilityUtil; import gw.internal.gosu.ir.transform.util.IRTypeResolver; import gw.internal.gosu.parser.BeanAccess; import gw.internal.gosu.parser.GosuVarPropertyInfo; import gw.internal.gosu.parser.JavaFieldPropertyInfo; import gw.internal.gosu.parser.JavaPropertyInfo; import gw.internal.gosu.parser.expressions.Identifier; import gw.internal.gosu.parser.statements.MemberAssignmentStatement; import gw.internal.gosu.runtime.GosuRuntimeMethods; import gw.lang.ir.IRExpression; import gw.lang.ir.IRStatement; import gw.lang.ir.IRType; import gw.lang.parser.IExpression; import gw.lang.parser.Keyword; import gw.lang.parser.exceptions.ParseException; import gw.lang.reflect.IMetaType; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IPropertyInfoDelegate; import gw.lang.reflect.IType; import gw.lang.reflect.gs.IGosuVarPropertyInfo; /** */ public class MemberAssignmentStatementTransformer extends AbstractStatementTransformer<MemberAssignmentStatement> { public static IRStatement compile( TopLevelTransformationContext cc, MemberAssignmentStatement stmt ) { MemberAssignmentStatementTransformer gen = new MemberAssignmentStatementTransformer( cc, stmt ); return gen.compile(); } private MemberAssignmentStatementTransformer( TopLevelTransformationContext cc, MemberAssignmentStatement stmt ) { super( cc, stmt ); } @Override protected IRStatement compile_impl() { String strMemberName = _stmt().getMemberName(); if( strMemberName == null ) { // If the name is false, it's of the form foo[bar] where bar is a variable. We have to do the access reflectively IRExpression memberNameExpression = ExpressionTransformer.compile( _stmt().getMemberExpression(), _cc() ); if( _stmt().getRootExpression().getType() instanceof IMetaType ) { // If it's a meta type, assume it's a static property return reflectivelySetProperty( _stmt().getRootExpression().getType(), memberNameExpression, null, false ); } else { return reflectivelySetProperty( _stmt().getRootExpression().getType(), memberNameExpression, ExpressionTransformer.compile( _stmt().getRootExpression(), _cc() ), true ); } } else { try { IPropertyInfo pi = BeanAccess.getPropertyInfo( _stmt().getRootExpression().getType(), strMemberName, null, null, null ); IRProperty irProperty = IRPropertyFactory.createIRProperty( pi ); IRType propertyType = irProperty.getType(); if( pi.isStatic() ) { return assignStaticMember( pi, irProperty, propertyType ); } else { return assignInstanceMember( pi, irProperty ); } } catch( ParseException e ) { throw new RuntimeException( e ); } } } private IRStatement assignInstanceMember( IPropertyInfo pi, IRProperty irProperty ) { IExpression rootExpr = _stmt().getRootExpression(); IRExpression root = pushRootExpression( getConcreteType( rootExpr.getType() ), rootExpr, irProperty ); if( isScopedField( pi ) ) { IGosuVarPropertyInfo propertyInfo = getActualPropertyInfo( pi ); return setScopedSymbolValue( propertyInfo, _stmt().getExpression() ); } else if( irProperty.isBytecodeProperty() ) { IRExpression rhs = compileRhs( irProperty ); if( irProperty.isField() ) { return setField( irProperty, root, rhs ); } else if( isWriteMethodMissingAndUsingLikeNamedField( irProperty ) ) { return setField( irProperty.getOwningIType(), getField( ((IRPropertyFromPropertyInfo)irProperty).getTerminalProperty() ), getWritableType( irProperty ), irProperty.getAccessibility(), root, rhs ); } else { if( isSuperCall( _stmt().getRootExpression() ) ) { return buildMethodCall( callSpecialMethod( getDescriptor( _cc().getSuperType() ), irProperty.getSetterMethod(), root, exprList( rhs ) ) ); } else { IRExpression irMethodCall = callMethod( irProperty.getSetterMethod(), root, exprList( rhs ) ); assignStructuralTypeOwner( rootExpr, irMethodCall ); return buildMethodCall( irMethodCall ); } } } else { return reflectivelySetProperty( pi.getOwnersType(), pushConstant( pi.getDisplayName() ), root, false ); } } private IRExpression pushRootExpression( IType rootType, IExpression rootExpr, IRProperty pi ) { // Push the root expression value IRExpression root = ExpressionTransformer.compile( rootExpr, _cc() ); //... and make sure it's boxed for the method call root = boxValue( rootType, root ); if( pi != null && !pi.isStatic() ) { IRType type = pi.getTargetRootIRType(); if( !type.isAssignableFrom( root.getType() ) ) { root = buildCast( type, root ); } } return root; } private boolean isWriteMethodMissingAndUsingLikeNamedField( IRProperty irPi ) { if( !(irPi instanceof IRPropertyFromPropertyInfo) ) { return false; } IPropertyInfo terminalPi = ((IRPropertyFromPropertyInfo)irPi).getTerminalProperty(); return terminalPi instanceof JavaPropertyInfo && isField( terminalPi ); } private IRExpression compileRhs( IRProperty pi ) { IRExpression rhs = ExpressionTransformer.compile( _stmt().getExpression(), _cc() ); if( !pi.isStatic() ) { IRType type = getWritableType( pi ); if( !type.isAssignableFrom( rhs.getType() ) ) { rhs = buildCast( type, rhs ); } } return rhs; } private IRType getWritableType( IRProperty pi ) { if( !(pi instanceof IRPropertyFromPropertyInfo) ) { return pi.getType(); } IRType type; IPropertyInfo terminalPi = ((IRPropertyFromPropertyInfo)pi).getTerminalProperty(); if( terminalPi instanceof JavaPropertyInfo && isField( terminalPi ) ) { type = IRTypeResolver.getDescriptor( ((JavaPropertyInfo)terminalPi).getPublicField().getType() ); } else { type = pi.getType(); } return type; } private IRStatement reflectivelySetProperty( IType type, IRExpression propertyName, IRExpression root, boolean forceDynamic ) { IRExpression value = ExpressionTransformer.compile( _stmt().getExpression(), _cc() ); IRExpression setter; if( forceDynamic || type instanceof IPlaceholder ) { // Placeholder types, such as snapshot types, have to get properties dynamically. They can't have static properties, though. if( root == null ) { throw new IllegalArgumentException( "Cannot invoke a static property reflectively on a placeholder type or via dynamic reflection" ); } setter = callStaticMethod( GosuRuntimeMethods.class, "setPropertyDynamically", new Class[]{Object.class, String.class, Object.class}, exprList( root, propertyName, value ) ); } else { // Everything else should dispatch to the statically-determined property setter = callStaticMethod( GosuRuntimeMethods.class, "setProperty", new Class[]{Object.class, IType.class, String.class, Object.class}, exprList( root, pushType( type ), propertyName, value ) ); } return buildMethodCall( setter ); } private IRStatement assignStaticMember( IPropertyInfo pi, IRProperty irProperty, IRType propertyType ) { // Unwrap the property, and use the real owner's type as the type to compile against while( pi instanceof IPropertyInfoDelegate ) { pi = ((IPropertyInfoDelegate)pi).getSource(); } IType rootType = pi.getOwnersType(); if( isScopedField( pi ) ) { IGosuVarPropertyInfo propertyInfo = getActualPropertyInfo( pi ); return setScopedSymbolValue( propertyInfo, _stmt().getExpression() ); } else { if( irProperty.isBytecodeProperty() ) { IRExpression rhs = compileRhs( irProperty ); if( irProperty.isField() ) { return setStaticField( rootType, getField( pi ), propertyType, AccessibilityUtil.forFeatureInfo( pi ), rhs ); } else { return buildMethodCall( callMethod( irProperty.getSetterMethod(), null, exprList( rhs ) ) ); } } else { return reflectivelySetProperty( pi.getOwnersType(), pushConstant( pi.getDisplayName() ), nullLiteral(), false ); } } } private boolean isField( IPropertyInfo pi ) { while( pi instanceof IPropertyInfoDelegate ) { pi = ((IPropertyInfoDelegate)pi).getSource(); } if( pi instanceof JavaPropertyInfo ) { JavaPropertyInfo jpi = (JavaPropertyInfo)pi; return jpi.getWriteMethodInfo() == null && jpi.getPublicField() != null; } return pi instanceof GosuVarPropertyInfo || pi instanceof JavaFieldPropertyInfo || ((pi instanceof IPropertyInfoDelegate) && isField( ((IPropertyInfoDelegate)pi).getSource() )); } private String getField( IPropertyInfo pi ) { if( !isField( pi ) ) { throw new IllegalArgumentException( pi.getName() + " is not a 'field' property" ); } while( pi instanceof IPropertyInfoDelegate ) { pi = ((IPropertyInfoDelegate)pi).getSource(); } if( pi instanceof JavaPropertyInfo ) { JavaPropertyInfo jpi = (JavaPropertyInfo)pi; return jpi.getPublicField().getName(); } if( pi.getClass() == JavaFieldPropertyInfo.class ) { return ((JavaFieldPropertyInfo)pi).getField().getName(); } return pi.getName(); } private boolean isSuperCall( IExpression rootExpr ) { return rootExpr instanceof Identifier && Keyword.KW_super.equals( ((Identifier)rootExpr).getSymbol().getName() ); } }