package php.runtime.invoke; import php.runtime.Memory; import php.runtime.common.Messages; import php.runtime.common.Modifier; import php.runtime.env.Environment; import php.runtime.env.TraceInfo; import php.runtime.exceptions.CriticalException; import php.runtime.exceptions.support.ErrorType; import php.runtime.invoke.cache.ConstantCallCache; import php.runtime.invoke.cache.PropertyCallCache; import php.runtime.lang.Closure; import php.runtime.lang.IObject; import php.runtime.memory.ArrayMemory; import php.runtime.memory.ObjectMemory; import php.runtime.memory.ReferenceMemory; import php.runtime.memory.StringMemory; import php.runtime.reflection.ClassEntity; import php.runtime.reflection.ConstantEntity; import php.runtime.reflection.MethodEntity; final public class ObjectInvokeHelper { private ObjectInvokeHelper() { } public static Memory invokeParentMethod(Memory object, String methodName, String methodLowerName, Environment env, TraceInfo trace, Memory[] args) throws Throwable { Memory[] passed = null; boolean doublePop = false; if (object.isNull()) { ClassEntity parent = env.__getParentClass(trace); return InvokeHelper.callStatic( env, trace, parent.getLowerName(), methodLowerName, parent.getName(), methodName, args, null, 0 ); } IObject iObject = ((ObjectMemory) object).value; ClassEntity childClazz = iObject.getReflection(); ClassEntity clazz = env.getLastClassOnStack().getParent(); MethodEntity method; if (clazz == null) { env.error(trace, "Cannot access parent:: when current class scope has no parent"); return Memory.NULL; } if (methodName == null) { method = childClazz.methodMagicInvoke != null ? childClazz.methodMagicInvoke : clazz.methodMagicInvoke; } else { method = clazz.findMethod(methodLowerName); if (method == null && (( method = childClazz.methodMagicCall != null ? childClazz.methodMagicCall : clazz.methodMagicCall) != null)) { passed = new Memory[]{new StringMemory(methodName), ArrayMemory.of(args)}; doublePop = true; } } String className = clazz.getName(); if (method == null) { if (methodName == null) methodName = "__invoke"; env.error(trace, ErrorType.E_ERROR, Messages.ERR_CALL_TO_UNDEFINED_METHOD.fetch( className + "::" + methodName ) ); return Memory.NULL; } InvokeHelper.checkAccess(env, trace, method); if (passed == null) { passed = InvokeHelper.makeArguments( env, args, method.getParameters(), className, methodName, trace ); } Memory result = method.getImmutableResult(); if (result != null) return result; try { if (trace != null) { env.pushCall(trace, iObject, args, methodName, method.getClazz().getName(), className); if (doublePop) env.pushCall(trace, iObject, passed, method.getName(), method.getClazz().getName(), className); } result = method.invokeDynamic(iObject, env, passed); } catch (ArrayIndexOutOfBoundsException e) { throw new CriticalException("Unable to call parent:: method " + className + "::" + methodName + "(), error = " + e.getMessage()); } finally { if (trace != null) { env.popCall(); if (doublePop) env.popCall(); } } return result; } public static Memory invokeMethod(Memory object, String methodName, Environment env, TraceInfo trace, Memory... args) throws Throwable { return invokeMethod(object, methodName, methodName.toLowerCase(), env, trace, args); } public static Memory invokeMethod(Memory object, String methodName, Environment env, Memory... args) throws Throwable { return invokeMethod(object, methodName, methodName.toLowerCase(), env, env.trace(), args); } public static Memory invokeMethod(Memory object, String methodName, String methodLowerName, Environment env, TraceInfo trace, Memory[] args) throws Throwable { object = object.toValue(); Memory[] passed = null; boolean doublePop = false; if (object.type != Memory.Type.OBJECT) { env.error(trace, ErrorType.E_RECOVERABLE_ERROR, Messages.ERR_CANNOT_CALL_OF_NON_OBJECT.fetch(methodName)); return Memory.NULL; } IObject iObject = ((ObjectMemory) object).value; ClassEntity clazz = iObject.getReflection(); MethodEntity method; if (methodName == null) { method = clazz.methodMagicInvoke; } else { method = clazz.findMethod(methodLowerName); if (method != null && method.isContextDepends()) { ClassEntity context = env.getLastClassOnStack(); if (context != null) { MethodEntity contextMethod = context.findMethod(methodLowerName); if (contextMethod != null) { method = contextMethod; } } } if (method == null && ((method = clazz.methodMagicCall) != null)) { clazz.methodMagicCall.setModifier(Modifier.PUBLIC); passed = new Memory[]{new StringMemory(methodName), ArrayMemory.of(args)}; doublePop = true; } } String className = clazz.getName(); if (method == null) { if (methodName == null) methodName = "__invoke"; env.error(trace, ErrorType.E_ERROR, Messages.ERR_CALL_TO_UNDEFINED_METHOD.fetch(className + "::" + methodName) ); return Memory.NULL; } InvokeHelper.checkAccess(env, trace, method); if (passed == null) { passed = InvokeHelper.makeArguments( env, args, method.getParameters(args == null ? 0 : args.length), className, methodName, trace ); } Memory result = method.getImmutableResult(); if (result != null) { return result; } try { if (trace != null) { String staticClass = className; if (iObject instanceof Closure) { staticClass = ((Closure) iObject).getScope(); } String stackClass = clazz.isHiddenInCallStack() ? staticClass : method.getClazz().getName(); env.pushCall(trace, iObject, args, methodName, stackClass, staticClass); if (doublePop) { env.pushCall(trace, iObject, passed, method.getName(), stackClass, staticClass); } } return method.invokeDynamic(iObject, env, passed); } catch (NoClassDefFoundError e) { throw new CriticalException("Unable to call method " + className + "::" + methodName + "(), " + e.getMessage()); } finally { if (trace != null) { env.popCall(); if (doublePop) env.popCall(); } } } public static Memory invokeMethod(IObject iObject, MethodEntity method, Environment env, TraceInfo trace, Memory[] args) throws Throwable { return invokeMethod(iObject, method, env, trace, args, true); } public static Memory invokeMethod(IObject iObject, MethodEntity method, Environment env, TraceInfo trace, Memory[] args, boolean checkAccess) throws Throwable { ClassEntity clazz = iObject.getReflection(); if (method == null) method = clazz.methodMagicInvoke; String className = clazz.getName(); if (method == null) { env.error(trace, Messages.ERR_CALL_TO_UNDEFINED_METHOD.fetch(className + "::__invoke")); return Memory.NULL; } if (checkAccess) InvokeHelper.checkAccess(env, trace, method); Memory[] passed = InvokeHelper.makeArguments( env, args, method.getParameters(args == null ? 0 : args.length), className, method.getName(), trace ); Memory result = method.getImmutableResult(); if (result != null) { return result; } if (trace != null) { String staticClass = className; if (iObject instanceof Closure) { staticClass = ((Closure) iObject).getScope(); } String stackClass = clazz.isHiddenInCallStack() ? staticClass : method.getClazz().getName(); env.pushCall(trace, iObject, args, method.getName(), stackClass, staticClass); } try { result = method.invokeDynamic(iObject, env, passed); } finally { if (trace != null) env.popCall(); } return result; } public static Memory emptyProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { object = object.toValue(); if (!object.isObject()) { return Memory.NULL; //env.error(trace, Messages.ERR_CANNOT_GET_PROPERTY_OF_NON_OBJECT.fetch(property)); } IObject iObject = ((ObjectMemory) object).value; return iObject.getReflection().emptyProperty(env, trace, iObject, property); } public static Memory issetProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { object = object.toValue(); if (!object.isObject()) { return Memory.NULL; //env.error(trace, Messages.ERR_CANNOT_GET_PROPERTY_OF_NON_OBJECT.fetch(property)); } IObject iObject = ((ObjectMemory) object).value; return iObject.getReflection().issetProperty(env, trace, iObject, property, callCache, cacheIndex); } public static void unsetProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { object = object.toValue(); if (!object.isObject()) { env.error(trace, Messages.ERR_CANNOT_GET_PROPERTY_OF_NON_OBJECT.fetch(property)); } IObject iObject = ((ObjectMemory) object).value; iObject.getReflection().unsetProperty(env, trace, iObject, property, callCache, cacheIndex); } public static Memory getConstant(String className, String lowerClassName, String constant, Environment env, TraceInfo trace, ConstantCallCache callCache, int cacheIndex) { ConstantEntity constantEntity = null; if (callCache != null) { constantEntity = callCache.get(env, cacheIndex); } if (constantEntity == null) { ClassEntity entity = env.fetchClass(className, lowerClassName, true); if (entity == null) { env.error(trace, Messages.ERR_CLASS_NOT_FOUND.fetch(className)); return Memory.NULL; } constantEntity = entity.findConstant(constant); if (constantEntity == null) { env.error(trace, Messages.ERR_UNDEFINED_CLASS_CONSTANT.fetch(constant)); return Memory.NULL; } if (callCache != null) { callCache.put(env, cacheIndex, constantEntity); } } Memory value = constantEntity.getValue(env); if (value == null) { return Memory.NULL; } return value; } public static Memory getProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { object = object.toValue(); if (!object.isObject()) { env.error(trace, Messages.ERR_CANNOT_GET_PROPERTY_OF_NON_OBJECT.fetch(property) ); return Memory.NULL; } IObject iObject = ((ObjectMemory) object).value; return iObject.getReflection().getProperty(env, trace, iObject, property, callCache, cacheIndex); } public static Memory getRefProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { object = object.toValue(); if (!object.isObject()) { env.error(trace, Messages.ERR_CANNOT_GET_PROPERTY_OF_NON_OBJECT.fetch(property) ); return Memory.NULL; } IObject iObject = ((ObjectMemory) object).value; return iObject.getReflection().getRefProperty(env, trace, iObject, property, callCache, cacheIndex); } public static Memory getStaticProperty(String className, String lowerClassName, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { ClassEntity entity = env.fetchClass(className, lowerClassName, true); if (entity == null) { env.error(trace, Messages.ERR_CLASS_NOT_FOUND.fetch(className)); return Memory.NULL; } return entity.getStaticProperty(env, trace, property, true, true, entity, callCache, cacheIndex); } public static Memory issetStaticProperty(String className, String lowerClassName, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { ClassEntity entity = env.fetchClass(className, lowerClassName, true); if (entity == null) { env.error(trace, Messages.ERR_CLASS_NOT_FOUND.fetch(className)); return Memory.NULL; } return entity.getStaticProperty(env, trace, property, false, true, entity, callCache, cacheIndex); } public static Memory unsetStaticProperty(String className, String lowerClassName, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { Memory get = getStaticProperty(className, lowerClassName, property, env, trace, callCache, cacheIndex); get.manualUnset(env); return Memory.NULL; } private static IObject fetchObject(Memory object, String property, Environment env, TraceInfo trace) { object = object.toValue(); if (!object.isObject()) { env.error(trace, Messages.ERR_CANNOT_SET_PROPERTY_OF_NON_OBJECT.fetch(property)); return null; } return ((ObjectMemory) object).value; } public static Memory incAndGetProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { return assignPlusProperty(object, Memory.CONST_INT_1, property, env, trace, callCache, cacheIndex); } public static Memory GetAndIncProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; ReferenceMemory ref = new ReferenceMemory(); iObject.getReflection().plusProperty(env, trace, iObject, property, Memory.CONST_INT_1, ref); return ref.value; } public static Memory decAndGetProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { return assignMinusProperty(object, Memory.CONST_INT_1, property, env, trace, callCache, cacheIndex); } public static Memory GetAndDecProperty(Memory object, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; ReferenceMemory ref = new ReferenceMemory(); iObject.getReflection().minusProperty(env, trace, iObject, property, Memory.CONST_INT_1, ref); return ref.value; } public static Memory assignProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().setProperty(env, trace, iObject, property, value, null, callCache, cacheIndex); } public static Memory assignPropertyRight(Memory value, String property, Environment env, TraceInfo trace, Memory object, PropertyCallCache callCache, int cacheIndex) throws Throwable { return assignProperty(object, value, property, env, trace, callCache, cacheIndex); } public static Memory assignPlusProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().plusProperty(env, trace, iObject, property, value, null); } public static Memory assignMinusProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().minusProperty(env, trace, iObject, property, value, null); } public static Memory assignMulProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().mulProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignDivProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().divProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignModProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().modProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignConcatProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().concatProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignBitAndProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().bitAndProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignBitOrProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().bitOrProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignBitXorProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().bitXorProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignBitShrProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().bitShrProperty(env, trace, iObject, property, value, callCache, cacheIndex); } public static Memory assignBitShlProperty(Memory object, Memory value, String property, Environment env, TraceInfo trace, PropertyCallCache callCache, int cacheIndex) throws Throwable { IObject iObject = fetchObject(object, property, env, trace); if (iObject == null) return Memory.NULL; return iObject.getReflection().bitShlProperty(env, trace, iObject, property, value, callCache, cacheIndex); } }