Java tutorial
/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * 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 java.util.ArrayList; import java.util.Iterator; import java.util.List; import lucee.commons.lang.StringUtil; import lucee.commons.lang.types.RefInteger; import lucee.commons.lang.types.RefIntegerImpl; import lucee.runtime.exp.TemplateException; import lucee.runtime.op.Constants; import lucee.runtime.type.scope.Scope; import lucee.runtime.type.scope.ScopeSupport; import lucee.runtime.type.util.ArrayUtil; import lucee.runtime.type.util.KeyConstants; import lucee.runtime.type.util.UDFUtil; import lucee.runtime.util.CallerUtil; import lucee.runtime.util.VariableUtilImpl; import lucee.transformer.bytecode.BytecodeContext; import lucee.transformer.bytecode.BytecodeException; import lucee.transformer.bytecode.Literal; import lucee.transformer.bytecode.Page; import lucee.transformer.bytecode.Position; import lucee.transformer.bytecode.cast.CastOther; import lucee.transformer.bytecode.expression.ExprString; import lucee.transformer.bytecode.expression.Expression; import lucee.transformer.bytecode.expression.ExpressionBase; import lucee.transformer.bytecode.expression.Invoker; import lucee.transformer.bytecode.literal.LitBoolean; import lucee.transformer.bytecode.literal.LitDouble; import lucee.transformer.bytecode.literal.LitString; import lucee.transformer.bytecode.util.ASMConstants; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.bytecode.util.ExpressionUtil; import lucee.transformer.bytecode.util.TypeScope; import lucee.transformer.bytecode.util.Types; import lucee.transformer.bytecode.visitor.ArrayVisitor; import lucee.transformer.library.function.FunctionLibFunction; import lucee.transformer.library.function.FunctionLibFunctionArg; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; public class Variable extends ExpressionBase implements Invoker { private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class); private static final Type CALLER_UTIL = Type.getType(CallerUtil.class); // java.lang.Object get(java.lang.String) final static Method METHOD_SCOPE_GET_KEY = new Method("get", Types.OBJECT, new Type[] { Types.COLLECTION_KEY }); // Object getCollection(java.lang.String) final static Method METHOD_SCOPE_GET_COLLECTION_KEY = new Method("getCollection", Types.OBJECT, new Type[] { Types.COLLECTION_KEY }); // java.lang.Object get(java.lang.String) final static Method METHOD_SCOPE_GET = new Method("get", Types.OBJECT, new Type[] { Types.STRING }); // Object getCollection(java.lang.String) /*final static Method METHOD_SCOPE_GET_COLLECTION= new Method("getCollection", Types.OBJECT, new Type[]{Types.STRING});*/ final static Method INIT = new Method("init", Types.COLLECTION_KEY, new Type[] { Types.STRING }); final static Method TO_KEY = new Method("toKey", Types.COLLECTION_KEY, new Type[] { Types.OBJECT }); final static Method[] METHODS_SCOPE_GET = new Method[6]; static { METHODS_SCOPE_GET[0] = METHOD_SCOPE_GET; METHODS_SCOPE_GET[1] = new Method("get", Types.OBJECT, new Type[] { Types.SCOPE, Types.STRING, Types.STRING }); METHODS_SCOPE_GET[2] = new Method("get", Types.OBJECT, new Type[] { Types.SCOPE, Types.STRING, Types.STRING, Types.STRING }); METHODS_SCOPE_GET[3] = new Method("get", Types.OBJECT, new Type[] { Types.SCOPE, Types.STRING, Types.STRING, Types.STRING, Types.STRING }); METHODS_SCOPE_GET[4] = new Method("get", Types.OBJECT, new Type[] { Types.SCOPE, Types.STRING, Types.STRING, Types.STRING, Types.STRING, Types.STRING }); METHODS_SCOPE_GET[5] = new Method("get", Types.OBJECT, new Type[] { Types.SCOPE, Types.STRING, Types.STRING, Types.STRING, Types.STRING, Types.STRING, Types.STRING }); } // Object getCollection (Object,String) /*private final static Method GET_COLLECTION = new Method("getCollection", Types.OBJECT, new Type[]{Types.OBJECT,Types.STRING});*/ // Object get (Object,String) /*private final static Method GET = new Method("get", Types.OBJECT, new Type[]{Types.OBJECT,Types.STRING});*/ //public Object get(PageContext pc,Object coll, Key[] keys, Object defaultValue) { private final static Method CALLER_UTIL_GET = new Method("get", Types.OBJECT, new Type[] { Types.PAGE_CONTEXT, Types.OBJECT, Types.COLLECTION_KEY_ARRAY, Types.OBJECT }); // Object getCollection (Object,String) private final static Method GET_COLLECTION_KEY = new Method("getCollection", Types.OBJECT, new Type[] { Types.OBJECT, Types.COLLECTION_KEY }); // Object get (Object,String) private final static Method GET_KEY = new Method("get", Types.OBJECT, new Type[] { Types.OBJECT, Types.COLLECTION_KEY }); 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 Type VARIABLE_UTIL_IMPL = Type.getType(VariableUtilImpl.class); private static final Method RECORDCOUNT = new Method("recordcount", Types.OBJECT, new Type[] { Types.PAGE_CONTEXT, Types.OBJECT }); private static final Method CURRENTROW = new Method("currentrow", Types.OBJECT, new Type[] { Types.PAGE_CONTEXT, Types.OBJECT }); private static final Method COLUMNLIST = new Method("columnlist", Types.OBJECT, new Type[] { Types.PAGE_CONTEXT, Types.OBJECT }); private static final Method THIS_GET = new Method("thisGet", Types.OBJECT, new Type[] {}); private static final Method THIS_TOUCH = new Method("thisTouch", Types.OBJECT, new Type[] {}); private static final Method THIS_GET_EL = new Method("thisGet", Types.OBJECT, new Type[] { Types.OBJECT }); private static final Method THIS_TOUCH_EL = new Method("thisTouch", Types.OBJECT, new Type[] { Types.OBJECT }); private static final Type CONSTANTS = Type.getType(Constants.class); int scope = Scope.SCOPE_UNDEFINED; List<Member> members = new ArrayList<Member>(); int countDM = 0; int countFM = 0; private boolean ignoredFirstMember; private boolean fromHash = false; private Expression defaultValue; private Boolean asCollection; public Variable(Position start, Position end) { super(start, end); } public Variable(int scope, Position start, Position end) { super(start, end); this.scope = scope; } public Expression getDefaultValue() { return defaultValue; } public void setDefaultValue(Expression defaultValue) { this.defaultValue = defaultValue; } public Boolean getAsCollection() { return asCollection; } public void setAsCollection(Boolean asCollection) { this.asCollection = asCollection; } /** * @return the scope */ public int getScope() { return scope; } /** * @param scope the scope to set */ public void setScope(int scope) { this.scope = scope; } public void addMember(Member member) { if (member instanceof DataMember) countDM++; else countFM++; members.add(member); } public Member removeMember(int index) { Member rtn = members.remove(index); if (rtn instanceof DataMember) countDM--; else countFM--; return rtn; } public final Type writeOutCollection(BytecodeContext bc, int mode) throws BytecodeException { ExpressionUtil.visitLine(bc, getStart()); Type type = _writeOut(bc, mode, Boolean.TRUE); ExpressionUtil.visitLine(bc, getEnd()); return type; } public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException { if (defaultValue != null && countFM == 0 && countDM != 0) return _writeOutCallerUtil(bc, mode); return _writeOut(bc, mode, asCollection); } private Type _writeOut(BytecodeContext bc, int mode, Boolean asCollection) throws BytecodeException { GeneratorAdapter adapter = bc.getAdapter(); final int count = countFM + countDM; // count 0 if (count == 0) return _writeOutEmpty(bc); boolean doOnlyScope = scope == Scope.SCOPE_LOCAL; //boolean last; for (int i = doOnlyScope ? 0 : 1; i < count; i++) { adapter.loadArg(0); } Type rtn = _writeOutFirst(bc, (members.get(0)), mode, count == 1, doOnlyScope, null, null); // pc.get( for (int i = doOnlyScope ? 0 : 1; i < count; i++) { Member member = (members.get(i)); boolean last = (i + 1) == count; // Data Member if (member instanceof DataMember) { ExprString name = ((DataMember) member).getName(); if (last && ASMUtil.isDotKey(name)) { LitString ls = (LitString) name; if (ls.getString().equalsIgnoreCase("RECORDCOUNT")) { adapter.invokeStatic(VARIABLE_UTIL_IMPL, RECORDCOUNT); } else if (ls.getString().equalsIgnoreCase("CURRENTROW")) { adapter.invokeStatic(VARIABLE_UTIL_IMPL, CURRENTROW); } else if (ls.getString().equalsIgnoreCase("COLUMNLIST")) { adapter.invokeStatic(VARIABLE_UTIL_IMPL, COLUMNLIST); } else { registerKey(bc, name); adapter.invokeVirtual(Types.PAGE_CONTEXT, asCollection(asCollection, last) ? GET_COLLECTION_KEY : GET_KEY); } } else { registerKey(bc, name); adapter.invokeVirtual(Types.PAGE_CONTEXT, asCollection(asCollection, last) ? GET_COLLECTION_KEY : GET_KEY); } rtn = Types.OBJECT; } // UDF else if (member instanceof UDF) { rtn = _writeOutUDF(bc, (UDF) member); } } return rtn; } private Type _writeOutCallerUtil(BytecodeContext bc, int mode) throws BytecodeException { GeneratorAdapter adapter = bc.getAdapter(); final int count = countFM + countDM; // count 0 if (count == 0) return _writeOutEmpty(bc); //boolean last; /*for(int i=doOnlyScope?0:1;i<count;i++) { adapter.loadArg(0); }*/ // pc //adapter.loadArg(0); adapter.loadArg(0); // collection RefInteger startIndex = new RefIntegerImpl(); _writeOutFirst(bc, (members.get(0)), mode, count == 1, true, defaultValue, startIndex); // keys Iterator<Member> it = members.iterator(); ArrayVisitor av = new ArrayVisitor(); av.visitBegin(adapter, Types.COLLECTION_KEY, countDM - startIndex.toInt()); int index = 0, i = 0; while (it.hasNext()) { DataMember member = (DataMember) it.next(); if (i++ < startIndex.toInt()) continue; av.visitBeginItem(adapter, index++); registerKey(bc, member.getName()); av.visitEndItem(bc.getAdapter()); } av.visitEnd(); // defaultValue defaultValue.writeOut(bc, MODE_REF); bc.getAdapter().invokeStatic(CALLER_UTIL, CALLER_UTIL_GET); return Types.OBJECT; } private boolean asCollection(Boolean asCollection, boolean last) { if (!last) return true; return asCollection != null && asCollection.booleanValue(); } public static void registerKey(BytecodeContext bc, Expression name) throws BytecodeException { registerKey(bc, name, false); } public static void registerKey(BytecodeContext bc, Expression name, boolean doUpperCase) throws BytecodeException { if (name instanceof Literal) { Literal l = (Literal) name; LitString ls = name instanceof LitString ? (LitString) l : LitString.toLitString(l.getString()); if (doUpperCase) { ls = ls.duplicate(); ls.upperCase(); } String key = KeyConstants.getFieldName(ls.getString()); if (key != null) { bc.getAdapter().getStatic(KEY_CONSTANTS, key, Types.COLLECTION_KEY); return; } int index = bc.registerKey(ls); bc.getAdapter().visitVarInsn(Opcodes.ALOAD, 0); bc.getAdapter().visitFieldInsn(Opcodes.GETFIELD, bc.getClassName(), "keys", Types.COLLECTION_KEY_ARRAY.toString()); bc.getAdapter().push(index); bc.getAdapter().visitInsn(Opcodes.AALOAD); return; } name.writeOut(bc, MODE_REF); bc.getAdapter().invokeStatic(Page.KEY_IMPL, INIT); //bc.getAdapter().invokeStatic(Types.CASTER, TO_KEY); return; } public static boolean canRegisterKey(Expression name) { return name instanceof LitString; } /** * outputs a empty Variable, only scope * Example: pc.formScope(); * @param adapter * @throws TemplateException */ private Type _writeOutEmpty(BytecodeContext bc) throws BytecodeException { if (ignoredFirstMember && (scope == Scope.SCOPE_LOCAL || scope == ScopeSupport.SCOPE_VAR)) return Types.VOID; GeneratorAdapter adapter = bc.getAdapter(); adapter.loadArg(0); Method m; Type t = Types.PAGE_CONTEXT; if (scope == Scope.SCOPE_ARGUMENTS) { LitBoolean.TRUE.writeOut(bc, MODE_VALUE); m = TypeScope.METHOD_ARGUMENT_BIND; } else if (scope == Scope.SCOPE_LOCAL) { t = Types.PAGE_CONTEXT; LitBoolean.TRUE.writeOut(bc, MODE_VALUE); m = TypeScope.METHOD_LOCAL_BIND; } else if (scope == ScopeSupport.SCOPE_VAR) { t = Types.PAGE_CONTEXT; LitBoolean.TRUE.writeOut(bc, MODE_VALUE); m = TypeScope.METHOD_VAR_BIND; } else m = TypeScope.METHODS[scope]; TypeScope.invokeScope(adapter, m, t); return m.getReturnType(); } private Type _writeOutFirst(BytecodeContext bc, Member member, int mode, boolean last, boolean doOnlyScope, Expression defaultValue, RefInteger startIndex) throws BytecodeException { if (member instanceof DataMember) return _writeOutFirstDataMember(bc, (DataMember) member, scope, last, doOnlyScope, defaultValue, startIndex); else if (member instanceof UDF) return _writeOutFirstUDF(bc, (UDF) member, scope, doOnlyScope); else return _writeOutFirstBIF(bc, (BIF) member, mode, last, getStart()); } static Type _writeOutFirstBIF(BytecodeContext bc, BIF bif, int mode, boolean last, Position line) throws BytecodeException { GeneratorAdapter adapter = bc.getAdapter(); adapter.loadArg(0); // class Class bifClass = bif.getClazz(); Type bifType = Type.getType(bifClass);//Types.toType(bif.getClassName()); Type rtnType = Types.toType(bif.getReturnType()); if (rtnType == Types.VOID) rtnType = Types.STRING; // arguments Argument[] args = bif.getArguments(); Type[] argTypes; // Arg Type FIX if (bif.getArgType() == FunctionLibFunction.ARG_FIX) { if (isNamed(bif.getName(), args)) { NamedArgument[] nargs = toNamedArguments(args); String[] names = new String[nargs.length]; // get all names for (int i = 0; i < nargs.length; i++) { names[i] = getName(nargs[i].getName()); } ArrayList<FunctionLibFunctionArg> list = bif.getFlf().getArg(); Iterator<FunctionLibFunctionArg> it = list.iterator(); argTypes = new Type[list.size() + 1]; argTypes[0] = Types.PAGE_CONTEXT; FunctionLibFunctionArg flfa; int index = 0; VT vt; while (it.hasNext()) { flfa = it.next(); vt = getMatchingValueAndType(flfa, nargs, names, line); if (vt.index != -1) names[vt.index] = null; argTypes[++index] = Types.toType(vt.type); if (vt.value == null) ASMConstants.NULL(bc.getAdapter()); else vt.value.writeOut(bc, Types.isPrimitiveType(argTypes[index]) ? MODE_VALUE : MODE_REF); } for (int y = 0; y < names.length; y++) { if (names[y] != null) { BytecodeException bce = new BytecodeException("argument [" + names[y] + "] is not allowed for function [" + bif.getFlf().getName() + "]", args[y].getStart()); UDFUtil.addFunctionDoc(bce, bif.getFlf()); throw bce; } } } else { argTypes = new Type[args.length + 1]; argTypes[0] = Types.PAGE_CONTEXT; for (int y = 0; y < args.length; y++) { argTypes[y + 1] = Types.toType(args[y].getStringType()); args[y].writeOutValue(bc, Types.isPrimitiveType(argTypes[y + 1]) ? MODE_VALUE : MODE_REF); } // if no method exists for the exact match of arguments, call the method with all arguments (when exists) if (methodExists(bifClass, "call", argTypes, rtnType) == Boolean.FALSE) { ArrayList<FunctionLibFunctionArg> _args = bif.getFlf().getArg(); Type[] tmp = new Type[_args.size() + 1]; // fill the existing for (int i = 0; i < argTypes.length; i++) { tmp[i] = argTypes[i]; } // get the rest with default values FunctionLibFunctionArg flfa; for (int i = argTypes.length; i < tmp.length; i++) { flfa = _args.get(i - 1); tmp[i] = Types.toType(flfa.getTypeAsString()); getDefaultValue(flfa).value.writeOut(bc, Types.isPrimitiveType(tmp[i]) ? MODE_VALUE : MODE_REF); } argTypes = tmp; } } } // Arg Type DYN else { argTypes = new Type[2]; argTypes[0] = Types.PAGE_CONTEXT; argTypes[1] = Types.OBJECT_ARRAY; ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, args); } adapter.invokeStatic(bifType, new Method("call", rtnType, argTypes)); if (mode == MODE_REF || !last) { if (Types.isPrimitiveType(rtnType)) { adapter.invokeStatic(Types.CASTER, new Method("toRef", Types.toRefType(rtnType), new Type[] { rtnType })); rtnType = Types.toRefType(rtnType); } } return rtnType; } /** * checks if a method exists * @param clazz * @param methodName * @param args * @param returnType * @return returns null when checking fi */ private static Boolean methodExists(Class clazz, String methodName, Type[] args, Type returnType) { try { //Class _clazz=Types.toClass(clazz); Class[] _args = new Class[args.length]; for (int i = 0; i < _args.length; i++) { _args[i] = Types.toClass(args[i]); } Class rtn = Types.toClass(returnType); try { java.lang.reflect.Method m = clazz.getMethod(methodName, _args); return m.getReturnType() == rtn; } catch (Exception e) { return false; } } catch (Exception e) { e.printStackTrace(); return null; } } static Type _writeOutFirstUDF(BytecodeContext bc, UDF udf, int scope, boolean doOnlyScope) throws BytecodeException { GeneratorAdapter adapter = bc.getAdapter(); // pc.getFunction (Object,String,Object[]) // pc.getFunctionWithNamedValues (Object,String,Object[]) adapter.loadArg(0); if (!doOnlyScope) adapter.loadArg(0); Type rtn = TypeScope.invokeScope(adapter, scope); if (doOnlyScope) return rtn; return _writeOutUDF(bc, udf); } private static Type _writeOutUDF(BytecodeContext bc, UDF udf) throws BytecodeException { registerKey(bc, udf.getName()); Argument[] args = udf.getArguments(); // no arguments if (args.length == 0) { bc.getAdapter().getStatic(CONSTANTS, "EMPTY_OBJECT_ARRAY", Types.OBJECT_ARRAY); } else ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, args); bc.getAdapter().invokeVirtual(Types.PAGE_CONTEXT, udf.hasNamedArgs() ? GET_FUNCTION_WITH_NAMED_ARGS_KEY : GET_FUNCTION_KEY); return Types.OBJECT; } Type _writeOutFirstDataMember(BytecodeContext bc, DataMember member, int scope, boolean last, boolean doOnlyScope, Expression defaultValue, RefInteger startIndex) throws BytecodeException { GeneratorAdapter adapter = bc.getAdapter(); if (startIndex != null) startIndex.setValue(doOnlyScope ? 0 : 1); // this if (scope == Scope.SCOPE_UNDEFINED) { ExprString name = member.getName(); if (ASMUtil.isDotKey(name)) { LitString ls = (LitString) name; if (ls.getString().equalsIgnoreCase("THIS")) { if (startIndex != null) startIndex.setValue(1); adapter.loadArg(0); adapter.checkCast(Types.PAGE_CONTEXT_IMPL); if (defaultValue != null) { defaultValue.writeOut(bc, MODE_REF); adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL, (countFM + countDM) == 1 ? THIS_GET_EL : THIS_TOUCH_EL); } else adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL, (countFM + countDM) == 1 ? THIS_GET : THIS_TOUCH); return Types.OBJECT; } } } // local Type rtn; if (scope == Scope.SCOPE_LOCAL && defaultValue != null) { adapter.loadArg(0); adapter.checkCast(Types.PAGE_CONTEXT_IMPL); LitBoolean.FALSE.writeOut(bc, MODE_VALUE); defaultValue.writeOut(bc, MODE_VALUE); adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL, TypeScope.METHOD_LOCAL_EL); rtn = Types.OBJECT; } else { adapter.loadArg(0); rtn = TypeScope.invokeScope(adapter, scope); } if (doOnlyScope) return rtn; registerKey(bc, member.getName()); adapter.invokeInterface(TypeScope.SCOPES[scope], !last && scope == Scope.SCOPE_UNDEFINED ? METHOD_SCOPE_GET_COLLECTION_KEY : METHOD_SCOPE_GET_KEY); return Types.OBJECT; } /** * @return the members */ public List<Member> getMembers() { return members; } /** * @return the first member or null if there no member */ public Member getFirstMember() { if (members.isEmpty()) return null; return members.get(0); } /** * @return the first member or null if there no member */ public Member getLastMember() { if (members.isEmpty()) return null; return members.get(members.size() - 1); } public void ignoredFirstMember(boolean b) { this.ignoredFirstMember = b; } public boolean ignoredFirstMember() { return ignoredFirstMember; } private static VT getMatchingValueAndType(FunctionLibFunctionArg flfa, NamedArgument[] nargs, String[] names, Position line) throws BytecodeException { String flfan = flfa.getName(); // first search if a argument match for (int i = 0; i < nargs.length; i++) { if (names[i] != null && names[i].equalsIgnoreCase(flfan)) { nargs[i].setValue(nargs[i].getRawValue(), flfa.getTypeAsString()); return new VT(nargs[i].getValue(), flfa.getTypeAsString(), i); } } // then check if a alias match String alias = flfa.getAlias(); if (!StringUtil.isEmpty(alias)) { //String[] arrAlias = lucee.runtime.type.List.toStringArray(lucee.runtime.type.List.trimItems(lucee.runtime.type.List.listToArrayRemoveEmpty(alias, ','))); for (int i = 0; i < nargs.length; i++) { if (names[i] != null && lucee.runtime.type.util.ListUtil.listFindNoCase(alias, names[i], ",") != -1) { nargs[i].setValue(nargs[i].getRawValue(), flfa.getTypeAsString()); return new VT(nargs[i].getValue(), flfa.getTypeAsString(), i); } } } // if not required return the default value if (!flfa.getRequired()) { return getDefaultValue(flfa); } BytecodeException be = new BytecodeException( "missing required argument [" + flfan + "] for function [" + flfa.getFunction().getName() + "]", line); UDFUtil.addFunctionDoc(be, flfa.getFunction()); throw be; } private static VT getDefaultValue(FunctionLibFunctionArg flfa) { String defaultValue = flfa.getDefaultValue(); String type = flfa.getTypeAsString(); if (defaultValue == null) { if (type.equals("boolean") || type.equals("bool")) return new VT(LitBoolean.FALSE, type, -1); if (type.equals("number") || type.equals("numeric") || type.equals("double")) return new VT(LitDouble.ZERO, type, -1); return new VT(null, type, -1); } return new VT(CastOther.toExpression(LitString.toExprString(defaultValue), type), type, -1); } private static String getName(Expression expr) throws BytecodeException { String name = ASMUtil.toString(expr); if (name == null) throw new BytecodeException( "cannot extract a string from a object of type [" + expr.getClass().getName() + "]", null); return name; } /** * translate a array of arguments to a araay of NamedArguments, attention no check if the elements are really named arguments * @param args * @return */ private static NamedArgument[] toNamedArguments(Argument[] args) { NamedArgument[] nargs = new NamedArgument[args.length]; for (int i = 0; i < args.length; i++) { nargs[i] = (NamedArgument) args[i]; } return nargs; } /** * check if the arguments are named arguments or regular arguments, throws a exception when mixed * @param funcName * @param args * @param line * @return * @throws BytecodeException */ private static boolean isNamed(Object funcName, Argument[] args) throws BytecodeException { if (ArrayUtil.isEmpty(args)) return false; boolean named = false; for (int i = 0; i < args.length; i++) { if (args[i] instanceof NamedArgument) named = true; else if (named) throw new BytecodeException("invalid argument for function " + funcName + ", you can not mix named and unnamed arguments", args[i].getStart()); } return named; } public void setFromHash(boolean fromHash) { this.fromHash = fromHash; } public boolean fromHash() { return fromHash; } } class VT { Expression value; String type; int index; public VT(Expression value, String type, int index) { this.value = value; this.type = type; this.index = index; } }