package php.runtime.memory; 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.lang.*; import php.runtime.lang.spl.ArrayAccess; import php.runtime.lang.spl.Traversable; import php.runtime.lang.spl.iterator.Iterator; import php.runtime.lang.spl.iterator.IteratorAggregate; import php.runtime.lang.support.ICloneableObject; import php.runtime.lang.support.IComparableObject; import php.runtime.lang.support.IManualDestructable; import php.runtime.memory.support.MemoryStringUtils; import php.runtime.reflection.ClassEntity; import php.runtime.reflection.PropertyEntity; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.HashSet; import java.util.Set; public class ObjectMemory extends Memory { public IObject value; public ObjectMemory() { super(Type.OBJECT); } public ObjectMemory(IObject object){ this(); this.value = object; } public static Memory valueOf(IObject object){ if (object == null) return NULL; return new ObjectMemory(object); } public ClassEntity getReflection(){ return value.getReflection(); } public ArrayMemory getProperties(){ return value.getProperties(); } @Override public int getPointer(boolean absolute) { return value.getPointer(); } @Override public int getPointer() { return value.getPointer(); } @Override public boolean isObject() { return true; } @Override public boolean isResource() { return value instanceof Resource; } @Override public long toLong() { return 0; } @Override public double toDouble() { return 0; } @Override public boolean toBoolean() { return true; } @Override public Memory toNumeric() { return CONST_INT_0; } @Override public String toString() { ClassEntity entity = value.getReflection(); if (entity.methodMagicToString != null){ Environment env = value.getEnvironment(); if (env == null) return "Object"; // We can't get real trace info from toString method :( env.pushCall( entity.methodMagicToString.getTrace(), value, null, entity.methodMagicToString.getName(), entity.getName(), null ); try { Memory result = entity.methodMagicToString.invokeDynamic(value, env, (Memory[]) null); if (!result.isString()){ env.error( ErrorType.E_RECOVERABLE_ERROR, "Method %s must return a string value", entity.methodMagicToString.getSignatureString(false) ); return ""; } return result.toString(); } catch (RuntimeException e) { throw e; } catch (Throwable e){ throw new RuntimeException(e); } finally { env.popCall(); } } else return "Object"; } @Override public Memory inc() { return CONST_INT_1; } @Override public Memory dec() { return CONST_INT_M1; } @Override public Memory negative() { return CONST_INT_0; } @Override public Memory plus(Memory memory) { return toNumeric().plus(memory); } @Override public Memory minus(Memory memory) { return toNumeric().minus(memory); } @Override public Memory mul(Memory memory) { return toNumeric().mul(memory); } @Override public Memory pow(Memory memory) { return toNumeric().pow(memory); } @Override public Memory div(Memory memory) { return toNumeric().div(memory); } private static interface Comparator { boolean compare(IObject o1, IObject o2); } private boolean compare(Memory other, Comparator comparator){ switch (other.type){ case OBJECT: ClassEntity otherReflection = ((ObjectMemory)other).getReflection(); if (otherReflection.getId() != getReflection().getId()) return false; IObject otherObject = ((ObjectMemory)other).value; return comparator.compare(value, otherObject); case REFERENCE: return compare(other.toValue(), comparator); } return false; } public int compare(IObject other, boolean strict, Set<Integer> used){ ClassEntity otherReflection = other.getReflection(); if (otherReflection.getId() != getReflection().getId()) return -2; if (used == null) used = new HashSet<Integer>(); try { if (used.add(other.getPointer())) return value.getProperties().compare(other.getProperties(), strict, used); return 0; } finally { used.remove(other.getPointer()); } } @Override public boolean equal(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__equal(o2); return o1.getProperties().equal(o2.getProperties()); } }); } @Override public boolean notEqual(Memory memory) { return !equal(memory); } @Override public boolean smaller(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__smaller(o2); return o1.getProperties().smaller(o2.getProperties()); } }); } @Override public boolean smallerEq(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__smallerEq(o2); return o1.getProperties().smallerEq(o2.getProperties()); } }); } @Override public boolean greater(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__greater(o2); return o1.getProperties().greater(o2.getProperties()); } }); } @Override public boolean greaterEq(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__greaterEq(o2); return o1.getProperties().greaterEq(o2.getProperties()); } }); } @Override public byte[] getBinaryBytes(Charset charset) { return toString().getBytes(charset); } @Override public boolean identical(Memory memory) { return compare(memory, new Comparator() { @Override public boolean compare(IObject o1, IObject o2) { if (o1 instanceof IComparableObject) return ((IComparableObject) o1).__identical(o2); return o1.getPointer() == o2.getPointer(); } }); } @Override public boolean identical(long value) { return false; } @Override public boolean identical(double value) { return false; } @Override public boolean identical(boolean value) { return false; } @Override public boolean identical(String value) { return false; } @Override public ForeachIterator getNewIterator(final Environment env, boolean getReferences, boolean getKeyReferences) { if (value instanceof IteratorAggregate) { return env.invokeMethodNoThrow(value, "getIterator").getNewIterator(env, getReferences, getKeyReferences); } else if (value instanceof Iterator) { final Iterator iterator = (Iterator)value; final String className = value.getReflection().getName(); final boolean isNative = value.getReflection().isInternal(); return new ForeachIterator(getReferences, getKeyReferences, false) { private boolean keyInit = false; private boolean needNext = false; private boolean rewind = false; @Override public void reset() { rewind(); } @Override protected boolean init() { return rewind(); } protected boolean rewind() { if (getReferences && value instanceof Generator) { if (!((Generator) value).isReturnReferences()) { env.exception(trace, "You can only iterate a generator by-reference if it declared that it yields by-reference"); } } if (!rewind){ if (!isNative) env.pushCall(trace, ObjectMemory.this.value, null, "rewind", className, null); try { return iterator.rewind(env).toValue() != FALSE; } finally { rewind = true; if (!isNative) env.popCall(); } } return true; } @Override protected boolean prevValue() { return false; } @Override protected boolean nextValue() { return true; } @Override public boolean next() { if (!rewind()) return false; boolean valid = false; keyInit = false; if (needNext){ if (!isNative) env.pushCall(trace, ObjectMemory.this.value, null, "next", className, null); try { iterator.next(env); } finally { if (!isNative) env.popCall(); } } needNext = true; if (!isNative) env.pushCall(trace, ObjectMemory.this.value, null, "valid", className, null); try { valid = iterator.valid(env).toBoolean(); if (valid) { if (!isNative) env.pushCall(trace, ObjectMemory.this.value, null, "current", className, null); try { currentValue = iterator.current(env); if (!getReferences) currentValue = currentValue.toImmutable(); } finally { if (!isNative) env.popCall(); } } else { rewind = false; } } finally { if (!isNative) env.popCall(); } return valid; } @Override public Object getKey() { return getMemoryKey(); } @Override public Memory getMemoryKey() { if (keyInit) return (Memory)currentKey; if (!isNative) env.pushCall(trace, ObjectMemory.this.value, null, "key", className, null); try { currentKey = iterator.key(env).toImmutable(); keyInit = true; return (Memory)currentKey; } finally { if (!isNative) env.popCall(); } } }; } else if (value instanceof Traversable) { return ((Traversable) value).getNewIterator(env, getReferences, getKeyReferences); } else { return new ForeachIterator(getReferences, getKeyReferences, false) { private ForeachIterator child; private ClassEntity reflection; private ClassEntity context; @Override protected boolean init() { context = env.getLastClassOnStack(); reflection = value.getReflection(); child = value.getProperties().foreachIterator(getReferences, getKeyReferences); return true; } @Override public void reset() { child.reset(); } @Override protected boolean nextValue() { while (true){ if (!child.next()) return false; Object key = child.getKey(); if (key instanceof String){ String keyS = (String)key; int pos = keyS.lastIndexOf('\0'); if (pos > -1) keyS = keyS.substring(pos + 1); PropertyEntity entity = reflection.isInstanceOf(context) ? context.properties.get(keyS) : reflection.properties.get(keyS); int accessFlag = entity == null ? 0 : entity.canAccess(env); if (accessFlag == 0){ currentKey = entity == null ? keyS : entity.getName(); break; } else continue; } break; } //currentKey = child.getKey(); currentValue = child.getValue(); return true; } @Override protected boolean prevValue() { return false; } }; } } @Override public boolean instanceOf(String className, String lowerClassName) { ClassEntity origin = value.getReflection(); if (!lowerClassName.isEmpty() && lowerClassName.charAt(0) == '\\') return origin.isInstanceOf(lowerClassName.substring(1)); else return origin.isInstanceOfLower(lowerClassName); } @Override public boolean instanceOf(String name) { return instanceOf(name, name.toLowerCase()); } @Override public Memory toObject(Environment env) { return this; } @Override public Memory clone(Environment env, TraceInfo trace) throws Throwable { if (value instanceof ICloneableObject) { return new ObjectMemory(((ICloneableObject) value).__clone(env, trace)); } return new ObjectMemory(value.getReflection().cloneObject(value, env, trace)); } @Override public Memory toArray() { ArrayMemory result = new ArrayMemory(); ArrayMemory props = value.getProperties(); ForeachIterator iterator = props == null ? null : props.foreachIterator(false, false); if (iterator == null) { return new ArrayMemory().toConstant(); } ClassEntity reflection = value.getReflection(); while (iterator.next()){ Object key = iterator.getKey(); Memory value = iterator.getValue().toImmutable(); if (key instanceof String){ String keyS = (String)key; PropertyEntity prop = reflection.properties.get(keyS); if (prop == null || prop.getModifier() == Modifier.PUBLIC){ result.refOfIndex(keyS).assign(iterator.getValue().toImmutable()); } else { if (prop.getModifier() == Modifier.PROTECTED){ result.refOfIndex("\0*\0" + keyS).assign(value); } else { result.refOfIndex("\0" + prop.getClazz().getName() + "\0" + keyS).assign(value); } } } else { result.refOfIndex(null, iterator.getMemoryKey()).assign(value); } } return result.toConstant(); } @Override public void manualUnset(Environment env) { ClassEntity entity = value.getReflection(); if (entity.methodDestruct != null) { if (!value.isFinalized()) { value.doFinalize(); env.pushCall(value, entity.methodDestruct.getName()); try { if (value instanceof IManualDestructable) { ((IManualDestructable) value).onManualDestruct(env); } entity.methodDestruct.invokeDynamic(value, env); } catch (InvocationTargetException e){ env.__throwException(e); } catch (RuntimeException e){ throw e; } catch (Throwable throwable) { throw new CriticalException(throwable); } finally { env.popCall(); } } } } private void invalidUseAsArray(TraceInfo trace){ Environment env = value.getEnvironment(); if (env != null){ env.error(trace == null ? getReflection().getTrace() : trace, ErrorType.E_ERROR, Messages.ERR_CANNOT_USE_OBJECT_AS_ARRAY, getReflection().getName() ); } } @Override public Memory refOfIndex(final TraceInfo trace, final Memory index) { if (value instanceof ArrayAccess){ return new ReferenceMemory(){ @Override public Memory assign(Memory memory) { Environment env = ObjectMemory.this.value.getEnvironment(); if (env != null && trace != null){ ArrayAccess array = (ArrayAccess)ObjectMemory.this.value; Memory[] args = new Memory[]{ index, memory }; env.pushCall(ObjectMemory.this.value, "offsetSet", args); try { array.offsetSet(env, args); } finally { env.popCall(); } } else invalidUseAsArray(trace); return memory; } @Override public Memory assignRef(Memory reference) { return assign(reference); } @Override public Memory assign(long memory) { return assign(LongMemory.valueOf(memory)); } @Override public Memory assign(String memory) { return assign(StringMemory.valueOf(memory)); } @Override public Memory assign(boolean memory) { return assign(TrueMemory.valueOf(memory)); } @Override public Memory assign(double memory) { return assign(DoubleMemory.valueOf(memory)); } @Override public Memory assignConcat(Memory memory) { return assign(toValue().concat(memory)); } @Override public Memory assignConcat(long memory) { return assign(toValue().concat(memory)); } @Override public Memory assignConcat(double memory) { return assign(toValue().concat(memory)); } @Override public Memory assignConcat(boolean memory) { return assign(toValue().concat(memory)); } @Override public Memory assignConcat(String memory) { return assign(toValue().concat(memory)); } @Override public Memory assignPlus(Memory memory) { value = toValue(); return super.assignPlus(memory); } @Override public Memory assignPlus(long memory) { value = toValue(); return super.assignPlus(memory); } @Override public Memory assignPlus(double memory) { value = toValue(); return super.assignPlus(memory); } @Override public Memory assignPlus(boolean memory) { value = toValue(); return super.assignPlus(memory); } @Override public Memory assignPlus(String memory) { value = toValue(); return super.assignPlus(memory); } @Override public Memory assignPlusRight(Memory memory) { value = toValue(); return super.assignPlusRight(memory); } @Override public Memory assignMinus(Memory memory) { value = toValue(); return super.assignMinus(memory); } @Override public Memory assignMinus(long memory) { value = toValue(); return super.assignMinus(memory); } @Override public Memory assignMinus(double memory) { value = toValue(); return super.assignMinus(memory); } @Override public Memory assignMinus(boolean memory) { value = toValue(); return super.assignMinus(memory); } @Override public Memory assignMinus(String memory) { value = toValue(); return super.assignMinus(memory); } @Override public Memory assignMinusRight(Memory memory) { value = toValue(); return super.assignMinusRight(memory); } @Override public Memory assignMul(Memory memory) { value = toValue(); return super.assignMul(memory); } @Override public Memory assignMul(long memory) { value = toValue(); return super.assignMul(memory); } @Override public Memory assignMul(double memory) { value = toValue(); return super.assignMul(memory); } @Override public Memory assignMul(boolean memory) { value = toValue(); return super.assignMul(memory); } @Override public Memory assignMul(String memory) { value = toValue(); return super.assignMul(memory); } @Override public Memory assignMulRight(Memory memory) { value = toValue(); return super.assignMulRight(memory); } @Override public Memory assignDiv(Memory memory) { value = toValue(); return super.assignDiv(memory); } @Override public Memory assignDiv(long memory) { value = toValue(); return super.assignDiv(memory); } @Override public Memory assignDiv(double memory) { value = toValue(); return super.assignDiv(memory); } @Override public Memory assignDiv(boolean memory) { value = toValue(); return super.assignDiv(memory); } @Override public Memory assignDiv(String memory) { value = toValue(); return super.assignDiv(memory); } @Override public Memory assignDivRight(Memory memory) { value = toValue(); return super.assignDivRight(memory); } @Override public Memory assignMod(Memory memory) { value = toValue(); return super.assignMod(memory); } @Override public Memory assignMod(long memory) { value = toValue(); return super.assignMod(memory); } @Override public Memory assignMod(double memory) { value = toValue(); return super.assignMod(memory); } @Override public Memory assignMod(boolean memory) { value = toValue(); return super.assignMod(memory); } @Override public Memory assignMod(String memory) { value = toValue(); return super.assignMod(memory); } @Override public Memory assignModRight(Memory memory) { value = toValue(); return super.assignModRight(memory); } @Override public Memory assignBitShr(Memory memory) { value = toValue(); return super.assignBitShr(memory); } @Override public Memory assignBitShr(long memory) { value = toValue(); return super.assignBitShr(memory); } @Override public Memory assignBitShr(double memory) { value = toValue(); return super.assignBitShr(memory); } @Override public Memory assignBitShr(boolean memory) { value = toValue(); return super.assignBitShr(memory); } @Override public Memory assignBitShr(String memory) { value = toValue(); return super.assignBitShr(memory); } @Override public Memory assignBitShrRight(Memory memory) { value = toValue(); return super.assignBitShrRight(memory); } @Override public Memory assignBitShl(Memory memory) { value = toValue(); return super.assignBitShl(memory); } @Override public Memory assignBitShl(long memory) { value = toValue(); return super.assignBitShl(memory); } @Override public Memory assignBitShl(double memory) { value = toValue(); return super.assignBitShl(memory); } @Override public Memory assignBitShl(boolean memory) { value = toValue(); return super.assignBitShl(memory); } @Override public Memory assignBitShl(String memory) { value = toValue(); return super.assignBitShl(memory); } @Override public Memory assignBitShlRight(Memory memory) { value = toValue(); return super.assignBitShlRight(memory); } @Override public Memory assignBitAnd(Memory memory) { value = toValue(); return super.assignBitAnd(memory); } @Override public Memory assignBitAnd(long memory) { value = toValue(); return super.assignBitAnd(memory); } @Override public Memory assignBitAnd(double memory) { value = toValue(); return super.assignBitAnd(memory); } @Override public Memory assignBitAnd(boolean memory) { value = toValue(); return super.assignBitAnd(memory); } @Override public Memory assignBitAnd(String memory) { value = toValue(); return super.assignBitAnd(memory); } @Override public Memory assignBitAndRight(Memory memory) { value = toValue(); return super.assignBitAndRight(memory); } @Override public Memory assignBitOr(Memory memory) { value = toValue(); return super.assignBitOr(memory); } @Override public Memory assignBitOr(long memory) { value = toValue(); return super.assignBitOr(memory); } @Override public Memory assignBitOr(double memory) { value = toValue(); return super.assignBitOr(memory); } @Override public Memory assignBitOr(boolean memory) { value = toValue(); return super.assignBitOr(memory); } @Override public Memory assignBitOr(String memory) { value = toValue(); return super.assignBitOr(memory); } @Override public Memory assignBitOrRight(Memory memory) { value = toValue(); return super.assignBitOrRight(memory); } @Override public Memory assignBitXor(Memory memory) { value = toValue(); return super.assignBitXor(memory); } @Override public Memory assignBitXor(long memory) { value = toValue(); return super.assignBitXor(memory); } @Override public Memory assignBitXor(double memory) { value = toValue(); return super.assignBitXor(memory); } @Override public Memory assignBitXor(boolean memory) { value = toValue(); return super.assignBitXor(memory); } @Override public Memory assignBitXor(String memory) { value = toValue(); return super.assignBitXor(memory); } @Override public Memory assignBitXorRight(Memory memory) { value = toValue(); return super.assignBitXorRight(memory); } @Override public Memory toValue() { return ObjectMemory.this.valueOfIndex(trace, index); } @Override public ReferenceMemory getReference() { Memory ret = toValue(); if (ret instanceof ReferenceMemory) return (ReferenceMemory)ret; else return new ReferenceMemory(); } @Override public Memory toImmutable() { return toValue().toImmutable(); } @Override public Memory inc() { return toValue().inc(); } @Override public Memory dec() { return toValue().dec(); } @Override public Memory valueOfIndex(TraceInfo trace, Memory index) { return toValue().valueOfIndex(trace, index); } @Override public Memory valueOfIndex(TraceInfo trace, long index) { return toValue().valueOfIndex(trace, index); } @Override public Memory valueOfIndex(TraceInfo trace, double index) { return toValue().valueOfIndex(trace, index); } @Override public Memory valueOfIndex(TraceInfo trace, String index) { return toValue().valueOfIndex(trace, index); } @Override public Memory valueOfIndex(TraceInfo trace, boolean index) { return toValue().valueOfIndex(trace, index); } @Override public Memory refOfPush(TraceInfo trace) { return toValue().refOfPush(trace); } @Override public Memory refOfIndexAsShortcut(TraceInfo trace, Memory index) { return toValue().refOfIndexAsShortcut(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, Memory index) { return toValue().refOfIndex(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, long index) { return toValue().refOfIndex(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, double index) { return toValue().refOfIndex(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, String index) { return toValue().refOfIndex(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, boolean index) { return toValue().refOfIndex(trace, index); } @Override public void unsetOfIndex(TraceInfo trace, Memory index) { toValue().unsetOfIndex(trace, index); } @Override public Memory issetOfIndex(TraceInfo trace, Memory index) { return toValue().issetOfIndex(trace, index); } @Override public Memory emptyOfIndex(TraceInfo trace, Memory index) { return toValue().emptyOfIndex(trace, index); } }; } else { invalidUseAsArray(trace); return new ReferenceMemory(); } } @Override public Memory refOfIndexAsShortcut(TraceInfo trace, Memory index) { return refOfIndex(trace, index); } @Override public Memory refOfIndex(TraceInfo trace, long index) { return refOfIndex(trace, LongMemory.valueOf(index)); } @Override public Memory refOfIndex(TraceInfo trace, double index) { return refOfIndex(trace, DoubleMemory.valueOf(index)); } @Override public Memory refOfIndex(TraceInfo trace, String index) { return refOfIndex(trace,StringMemory.valueOf(index)); } @Override public Memory refOfIndex(TraceInfo trace, boolean index) { return refOfIndex(trace, index ? TRUE : FALSE); } @Override public Memory refOfPush(TraceInfo trace) { if (value instanceof ArrayAccess){ return refOfIndex(trace, NULL); } else { invalidUseAsArray(trace); return new ReferenceMemory(); } } @Override public Memory valueOfIndex(TraceInfo trace, Memory index) { if (value instanceof ArrayAccess){ Environment env = value.getEnvironment(); if (env != null && trace != null){ Memory[] args = new Memory[] { index }; env.pushCall(value, "offsetGet", args); try { return ((ArrayAccess) value).offsetGet(env, args); } finally { env.popCall(); } } else { invalidUseAsArray(trace); return NULL; } } else { invalidUseAsArray(trace); return NULL; } } @Override public Memory valueOfIndex(TraceInfo trace, boolean index) { return valueOfIndex(trace, TrueMemory.valueOf(index)); } @Override public Memory valueOfIndex(TraceInfo trace, long index) { return valueOfIndex(trace, LongMemory.valueOf(index)); } @Override public Memory valueOfIndex(TraceInfo trace, double index) { return valueOfIndex(trace, DoubleMemory.valueOf(index)); } @Override public Memory valueOfIndex(TraceInfo trace, String index) { return valueOfIndex(trace, StringMemory.valueOf(index)); } @Override public void unsetOfIndex(TraceInfo trace, Memory index) { if (value instanceof ArrayAccess){ Environment env = value.getEnvironment(); if (env != null && trace != null){ Memory[] args = new Memory[]{index}; env.pushCall(value, "offsetUnset", args); try { ((ArrayAccess) value).offsetUnset(env, args); } finally { env.popCall(); } } else invalidUseAsArray(trace); } else invalidUseAsArray(trace); } @Override public Memory emptyOfIndex(TraceInfo trace, Memory index) { return issetOfIndex(trace, index, true); } @Override public Memory issetOfIndex(TraceInfo trace, Memory index) { return issetOfIndex(trace, index, false); } private Memory issetOfIndex(TraceInfo trace, Memory index, boolean asEmpty) { if (value instanceof ArrayAccess){ Environment env = value.getEnvironment(); if (env != null && trace != null){ Memory[] args = new Memory[]{index}; env.pushCall(value, "offsetExists", args); try { if (((ArrayAccess) value).offsetExists(env, args).toBoolean()) return asEmpty ? valueOfIndex(trace, index) : TRUE; else return NULL; } finally { env.popCall(); } } else { invalidUseAsArray(trace); return NULL; } } else { invalidUseAsArray(trace); return NULL; } } @Override public boolean isClosure() { return value instanceof Closure; } }