/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.transformer.bytecode.expression.var; import lucee.runtime.type.scope.Scope; import lucee.runtime.type.scope.ScopeFactory; import lucee.transformer.Position; import lucee.transformer.TransformerException; import lucee.transformer.bytecode.BytecodeContext; import lucee.transformer.bytecode.expression.ExpressionBase; import lucee.transformer.bytecode.util.ExpressionUtil; import lucee.transformer.bytecode.util.TypeScope; import lucee.transformer.bytecode.util.Types; import lucee.transformer.expression.Expression; import lucee.transformer.expression.var.DataMember; import lucee.transformer.expression.var.Member; import lucee.transformer.expression.var.Variable; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; public class Assign extends ExpressionBase { // java.lang.Object set(String,Object) private final static Method METHOD_SCOPE_SET_KEY = new Method("set", Types.OBJECT, new Type[]{Types.COLLECTION_KEY,Types.OBJECT}); // .setArgument(obj) private final static Method SET_ARGUMENT = new Method("setArgument", Types.OBJECT, new Type[]{Types.OBJECT}); // Object touch (Object,String) private final static Method TOUCH_KEY = new Method("touch", Types.OBJECT, new Type[]{Types.OBJECT,Types.COLLECTION_KEY}); //Object set (Object,String,Object) private final static Method SET_KEY = new Method("set", Types.OBJECT, new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT}); // Object getFunction (Object,String,Object[]) private final static Method GET_FUNCTION_KEY = new Method("getFunction", Types.OBJECT, new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); // Object getFunctionWithNamedValues (Object,String,Object[]) private final static Method GET_FUNCTION_WITH_NAMED_ARGS_KEY = new Method("getFunctionWithNamedValues", Types.OBJECT, new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); private static final Method DATA_MEMBER_INIT = new Method("<init>", Types.VOID, new Type[]{Types.INT_VALUE,Types.INT_VALUE,Types.OBJECT}); private final Variable variable; private final Expression value; private int access=-1; private int modifier=0; /** * Constructor of the class * @param variable * @param value */ public Assign(Variable variable, Expression value, Position end) { super(variable.getFactory(),variable.getStart(),end); this.variable=variable; this.value=value; if(value instanceof Variable) ((Variable)value).assign(this); //this.returnOldValue=returnOldValue; } @Override public Type _writeOut(BytecodeContext bc, int mode) throws TransformerException { GeneratorAdapter adapter = bc.getAdapter(); int count=variable.getCount(); // count 0 if(count==0){ if(variable.ignoredFirstMember() && variable.getScope()==Scope.SCOPE_VAR){ //print.dumpStack(); return Types.VOID; } return _writeOutEmpty(bc); } boolean doOnlyScope=variable.getScope()==Scope.SCOPE_LOCAL; Type rtn=Types.OBJECT; //boolean last; for(int i=doOnlyScope?0:1;i<count;i++) { adapter.loadArg(0); } rtn=_writeOutFirst(bc, (variable.getMembers().get(0)),mode,count==1,doOnlyScope); // pc.get( for(int i=doOnlyScope?0:1;i<count;i++) { Member member=(variable.getMembers().get(i)); boolean last=(i+1)==count; // Data Member if(member instanceof DataMember) { //((DataMember)member).getName().writeOut(bc, MODE_REF); getFactory().registerKey(bc, ((DataMember)member).getName(),false); if(last)writeValue(bc); adapter.invokeVirtual(Types.PAGE_CONTEXT,last?SET_KEY:TOUCH_KEY); rtn=Types.OBJECT; } // UDF else if(member instanceof UDF) { if(last)throw new TransformerException("can't assign value to a user defined function",getStart()); UDF udf=(UDF) member; getFactory().registerKey(bc, udf.getName(),false); ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, udf.getArguments()); adapter.invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS_KEY:GET_FUNCTION_KEY); rtn=Types.OBJECT; } } return rtn; } private void writeValue(BytecodeContext bc) throws TransformerException { // set Access if((access>-1 || modifier>0)) { GeneratorAdapter ga = bc.getAdapter(); ga.newInstance(Types.DATA_MEMBER); ga.dup(); ga.push(access); ga.push(modifier); value.writeOut(bc, MODE_REF); ga.invokeConstructor(Types.DATA_MEMBER, DATA_MEMBER_INIT); } else value.writeOut(bc, MODE_REF); } private Type _writeOutFirst(BytecodeContext bc, Member member, int mode, boolean last, boolean doOnlyScope) throws TransformerException { if(member instanceof DataMember) { return _writeOutOneDataMember(bc,(DataMember)member,last,doOnlyScope); //return Variable._writeOutFirstDataMember(adapter,(DataMember)member,variable.scope, last); } else if(member instanceof UDF) { if(last)throw new TransformerException("can't assign value to a user defined function",getStart()); return VariableImpl._writeOutFirstUDF(bc,(UDF)member,variable.getScope(),doOnlyScope); } else { if(last)throw new TransformerException("can't assign value to a built in function",getStart()); return VariableImpl._writeOutFirstBIF(bc,(BIF)member,mode,last,getStart()); } } private Type _writeOutOneDataMember(BytecodeContext bc, DataMember member,boolean last, boolean doOnlyScope) throws TransformerException { GeneratorAdapter adapter = bc.getAdapter(); if(doOnlyScope){ adapter.loadArg(0); if(variable.getScope()==Scope.SCOPE_LOCAL){ return TypeScope.invokeScope(adapter, TypeScope.METHOD_LOCAL_TOUCH,Types.PAGE_CONTEXT); } return TypeScope.invokeScope(adapter, variable.getScope()); } // pc.get adapter.loadArg(0); if(last) { TypeScope.invokeScope(adapter, variable.getScope()); getFactory().registerKey(bc, member.getName(),false); writeValue(bc); adapter.invokeInterface(TypeScope.SCOPES[variable.getScope()],METHOD_SCOPE_SET_KEY); } else { adapter.loadArg(0); TypeScope.invokeScope(adapter, variable.getScope()); getFactory().registerKey(bc, member.getName(),false); adapter.invokeVirtual(Types.PAGE_CONTEXT,TOUCH_KEY); } return Types.OBJECT; } private Type _writeOutEmpty(BytecodeContext bc) throws TransformerException { GeneratorAdapter adapter = bc.getAdapter(); if(variable.getScope()==Scope.SCOPE_ARGUMENTS) { adapter.loadArg(0); TypeScope.invokeScope(adapter, Scope.SCOPE_ARGUMENTS); writeValue(bc); adapter.invokeInterface(TypeScope.SCOPE_ARGUMENT,SET_ARGUMENT); } else { adapter.loadArg(0); TypeScope.invokeScope(adapter, Scope.SCOPE_UNDEFINED); getFactory().registerKey(bc,bc.getFactory().createLitString(ScopeFactory.toStringScope(variable.getScope(),"undefined")),false); writeValue(bc); adapter.invokeInterface(TypeScope.SCOPES[Scope.SCOPE_UNDEFINED],METHOD_SCOPE_SET_KEY); } return Types.OBJECT; } /** * @return the value */ public Expression getValue() { return value; } /** * @return the variable */ public Variable getVariable() { return variable; } public void setAccess(int access) { this.access=access; } public int getAccess() { return access; } public void setModifier(int modifier) { this.modifier=modifier; } public int getModifier() { return modifier; } public void setFinal(boolean _final) { // TODO Auto-generated method stub } }