diff --git a/core/src/main/java/lucee/transformer/bytecode/op/OpElvis.java b/core/src/main/java/lucee/transformer/bytecode/op/OpElvis.java index d491db0aa1..329b8170d4 100644 --- a/core/src/main/java/lucee/transformer/bytecode/op/OpElvis.java +++ b/core/src/main/java/lucee/transformer/bytecode/op/OpElvis.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2014, the Railo Company Ltd. - * Copyright (c) 2015, Lucee Assosication Switzerland + * Copyright (c) 2015, Lucee Association Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,12 +28,11 @@ import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; +import lucee.runtime.functions.other.CreateUniqueId; import lucee.runtime.op.Elvis; import lucee.transformer.TransformerException; import lucee.transformer.bytecode.BytecodeContext; import lucee.transformer.bytecode.expression.ExpressionBase; -import lucee.transformer.bytecode.expression.var.BIF; -import lucee.transformer.bytecode.util.ASMConstants; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.bytecode.util.Types; import lucee.transformer.bytecode.visitor.ArrayVisitor; @@ -41,7 +40,6 @@ import lucee.transformer.expression.literal.Literal; import lucee.transformer.expression.var.DataMember; import lucee.transformer.expression.var.Member; -import lucee.transformer.expression.var.NamedMember; import lucee.transformer.expression.var.Variable; public final class OpElvis extends ExpressionBase { @@ -54,52 +52,59 @@ public final class OpElvis extends ExpressionBase { private Variable left; private Expression right; - /** - * - * @see lucee.transformer.bytecode.expression.ExpressionBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter, - * int) - */ @Override public Type _writeOut(BytecodeContext bc, int mode) throws TransformerException { if (ASMUtil.hasOnlyDataMembers(left)) return _writeOutPureDataMember(bc, mode); - Label notNull = new Label(); - Label end = new Label(); - Label labelMatch = new Label(); - Label labelEnd = new Label(); - + String name = createRandom(bc); GeneratorAdapter ga = bc.getAdapter(); - if (checkFunction(bc)) { - ga.visitJumpInsn(Opcodes.IFNONNULL, labelMatch); + ga.loadThis(); // Load 'this' onto the stack + ga.loadArg(0); + ga.visitMethodInsn(Opcodes.INVOKEVIRTUAL, bc.getClassName(), name, "(Llucee/runtime/PageContext;)Ljava/lang/Object;"); - ASMConstants.NULL(ga); - ga.goTo(labelEnd); - } + return Types.OBJECT; + } - // Label for test1() - ga.visitLabel(labelMatch); + private String createRandom(BytecodeContext parent) throws TransformerException { + String name = "el" + CreateUniqueId.invoke().toUpperCase() + Long.toString(System.currentTimeMillis(), Character.MAX_RADIX); - int l = ga.newLocal(Types.OBJECT); + Method m = new Method(name, Types.OBJECT, new Type[] { Types.PAGE_CONTEXT }); + GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, m, null, new Type[] { Types.THROWABLE }, parent.getClassWriter()); + BytecodeContext bc = new BytecodeContext(parent.getConstructor(), parent.getKeys(), parent, ga, m); + + Label tryStart = new Label(); + Label tryEnd = new Label(); + Label catchBlock = new Label(); + Label returnDefault = new Label(); + + ga.mark(tryStart); + // left bc.visitLine(left.getStart()); left.writeOut(bc, MODE_REF); bc.visitLine(left.getEnd()); - // End label - ga.visitLabel(labelEnd); - ga.dup(); - ga.storeLocal(l); + ga.visitJumpInsn(Opcodes.IFNULL, catchBlock); + // ga.loadLocal(localVal); + ga.returnValue(); + ga.mark(tryEnd); + ga.goTo(returnDefault); + + ga.mark(catchBlock); + ga.pop(); + ga.goTo(returnDefault); - ga.visitJumpInsn(Opcodes.IFNONNULL, notNull); + ga.visitTryCatchBlock(tryStart, tryEnd, catchBlock, "java/lang/Exception"); + + ga.mark(returnDefault); bc.visitLine(right.getStart()); right.writeOut(bc, MODE_REF); bc.visitLine(right.getEnd()); - ga.visitJumpInsn(Opcodes.GOTO, end); - ga.visitLabel(notNull); - ga.loadLocal(l); - ga.visitLabel(end); + ga.returnValue(); - return Types.OBJECT; + ga.endMethod(); + + return name; } public Type _writeOutPureDataMember(BytecodeContext bc, int mode) throws TransformerException { @@ -180,79 +185,6 @@ public Type _writeOutPureDataMember(BytecodeContext bc, int mode) throws Transfo } - private boolean checkFunction(BytecodeContext bc) throws TransformerException { - GeneratorAdapter adapter = bc.getAdapter(); - - List members = left.getMembers(); - int len = members.size(); - // to array - Iterator it = members.iterator(); - - List list = new ArrayList(); - Member m; - int index = 0; - while (it.hasNext()) { - m = it.next(); - index++; - if (!(m instanceof NamedMember)) { - return false;// throw new TransformerException(bc, "The Elvis Operator is not compatible with the given - // expression type: [" + m.getClass().getName() + "]", getEnd()); - } - // we only allow for this code that a function is at the end - if (index < len && !(m instanceof DataMember)) { - return false; - } - if (m instanceof BIF) { - return false; - // throw new TransformerException(bc, "Built-in function [" + ((BIF) m).getName() + "] cannot be - // used as the left operand in an Elvis operation.", getEnd()); - } - list.add((NamedMember) m); - } - NamedMember[] arr = list.toArray(new NamedMember[members.size()]); - - bc.visitLine(left.getStart()); - - // public static boolean call(PageContext pc , double scope,String[] varNames) - // pc - adapter.loadArg(0); - // scope - adapter.push((double) left.getScope()); - // varNames - - // all literal string? - boolean allLiteral = true; - for (int i = 0; i < arr.length; i++) { - if (!(arr[i].getName() instanceof Literal)) allLiteral = false; - } - - ArrayVisitor av = new ArrayVisitor(); - if (!allLiteral) { - // String Array - av.visitBegin(adapter, Types.STRING, arr.length); - for (int i = 0; i < arr.length; i++) { - av.visitBeginItem(adapter, i); - arr[i].getName().writeOut(bc, MODE_REF); - av.visitEndItem(adapter); - } - } - else { - // Collection.Key Array - av.visitBegin(adapter, Types.COLLECTION_KEY, arr.length); - for (int i = 0; i < arr.length; i++) { - av.visitBeginItem(adapter, i); - getFactory().registerKey(bc, arr[i].getName(), false); - av.visitEndItem(adapter); - } - } - av.visitEnd(); - - adapter.invokeStatic(ELVIS, allLiteral ? INVOKE_KEY : INVOKE_STR); - - bc.visitLine(left.getEnd()); - return true; - } - private OpElvis(Variable left, Expression right) { super(left.getFactory(), left.getStart(), right.getEnd()); this.left = left; diff --git a/loader/build.xml b/loader/build.xml index 07703fb1dc..a86a8b780a 100644 --- a/loader/build.xml +++ b/loader/build.xml @@ -2,7 +2,7 @@ - + diff --git a/loader/pom.xml b/loader/pom.xml index dd6f931ab1..52daa97d1f 100644 --- a/loader/pom.xml +++ b/loader/pom.xml @@ -3,7 +3,7 @@ org.lucee lucee - 6.0.2.33-SNAPSHOT + 6.0.2.34-SNAPSHOT jar Lucee Loader Build diff --git a/test/tickets/LDEV4826.cfc b/test/tickets/LDEV4826.cfc new file mode 100644 index 0000000000..a9aa3600d0 --- /dev/null +++ b/test/tickets/LDEV4826.cfc @@ -0,0 +1,59 @@ +component extends = "org.lucee.cfml.test.LuceeTestCase" { + + + function run( testResults, testBox ){ + describe( "Test case for LDEV-4826", function(){ + + it( title = "struct method call for existing value", body = function( currentSpec ){ + var sct = {}; + sct["key"]="Susi"; + expect( sct.get( "key" ) ?: "EMPTY" ).toBe( "Susi" ); + }); + + it( title = "struct method call for existing value containg null", body = function( currentSpec ){ + var sct = {}; + sct["nulls"]=nullValue(); + expect( sct.get( "nulls" ) ?: "EMPTY" ).toBe( "EMPTY" ); + }); + + it( title = "struct method call for NOT existing value ", body = function( currentSpec ){ + var sct = {}; + sct["nulls"]=nullValue(); + expect( sct.get( "nulls" ) ?: "EMPTY" ).toBe( "EMPTY" ); + }); + + + + it( title = "ConcurrentHashMap method call for existing value", body = function( currentSpec ){ + var chm = createObject( "java", "java.util.concurrent.ConcurrentHashMap" ).init(); + chm.put("key","Susi"); + expect( chm.get( "key" ) ?: "EMPTY" ).toBe( "Susi" ); + }); + + it( title = "ConcurrentHashMap method call for NOT existing value ", body = function( currentSpec ){ + var chm = createObject( "java", "java.util.concurrent.ConcurrentHashMap" ).init(); + expect( chm.get( "undefined" ) ?: "EMPTY" ).toBe( "EMPTY" ); + }); + + + + it( title = "struct get existing value", body = function( currentSpec ) { + var sct = {}; + sct["key"]="Susi"; + expect( sct["key"] ?: "EMPTY" ).toBe( "Susi" ); + expect( sct.key ?: "EMPTY" ).toBe( "Susi" ); + }); + + it( title = "struct get NOT existing value", body = function( currentSpec ) { + var sct = {}; + expect( sct["undefined"] ?: "EMPTY" ).toBe( "EMPTY" ); + expect( sct.undefined ?: "EMPTY" ).toBe( "EMPTY" ); + }); + + it( title = "function calls", body = function( currentSpec ) { + expect( susi().sorglos() ?: "EMPTY" ).toBe( "EMPTY" ); + }); + + }); + } +} \ No newline at end of file