package php.runtime.reflection;
import php.runtime.Memory;
import php.runtime.annotation.Reflection;
import php.runtime.common.LangMode;
import php.runtime.common.Messages;
import php.runtime.common.Modifier;
import php.runtime.common.StringUtils;
import php.runtime.env.ConcurrentEnvironment;
import php.runtime.env.Context;
import php.runtime.env.Environment;
import php.runtime.env.TraceInfo;
import php.runtime.exceptions.CriticalException;
import php.runtime.exceptions.FatalException;
import php.runtime.exceptions.support.ErrorException;
import php.runtime.exceptions.support.ErrorType;
import php.runtime.ext.support.Extension;
import php.runtime.invoke.InvokeArgumentHelper;
import php.runtime.invoke.ObjectInvokeHelper;
import php.runtime.invoke.cache.PropertyCallCache;
import php.runtime.lang.ForeachIterator;
import php.runtime.lang.IObject;
import php.runtime.lang.support.MagicSignatureClass;
import php.runtime.memory.ArrayMemory;
import php.runtime.memory.ReferenceMemory;
import php.runtime.memory.StringMemory;
import php.runtime.reflection.support.Entity;
import php.runtime.reflection.support.ReflectionUtils;
import php.runtime.wrap.ClassWrapper;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
public class ClassEntity extends Entity implements Cloneable {
private final static int FLAG_GET = 4000;
private final static int FLAG_SET = 4001;
private final static int FLAG_ISSET = 4002;
private final static int FLAG_UNSET = 4003;
// types
public enum Type {
CLASS, INTERFACE, TRAIT
}
private long id;
protected int methodCounts = 0;
protected boolean isInternal;
protected boolean isNotRuntime;
protected Extension extension;
protected Class<?> nativeClazz;
protected Constructor nativeConstructor;
protected Method nativeInitEnvironment;
protected ModuleEntity module;
protected final Map<String, MethodEntity> methods;
public MethodEntity methodConstruct;
public MethodEntity methodDestruct;
public MethodEntity methodMagicSet;
public MethodEntity methodMagicGet;
public MethodEntity methodMagicUnset;
public MethodEntity methodMagicIsset;
public MethodEntity methodMagicCall;
public MethodEntity methodMagicCallStatic;
public MethodEntity methodMagicInvoke;
public MethodEntity methodMagicToString;
public MethodEntity methodMagicClone;
public MethodEntity methodMagicSleep;
public MethodEntity methodMagicWakeup;
public MethodEntity methodMagicDebugInfo;
protected MethodEntity constructor;
protected final Map<String, ClassEntity> interfaces;
protected final Map<String, ClassEntity> traits;
public final Map<String, ConstantEntity> constants;
public final Map<String, PropertyEntity> properties;
public final Map<String, PropertyEntity> staticProperties;
public final Set<String> instanceOfList = new HashSet<String>();
protected ClassEntity parent;
protected DocumentComment docComment;
protected boolean isAbstract = false;
protected boolean isFinal = false;
protected Type type = Type.CLASS;
protected boolean isStatic;
protected static final ClassEntity magicSignatureClass =
new ClassEntity(new ClassWrapper(null, MagicSignatureClass.class));
public ClassEntity(Context context) {
super(context);
this.methods = new LinkedHashMap<String, MethodEntity>();
this.interfaces = new LinkedHashMap<String, ClassEntity>();
this.traits = new LinkedHashMap<String, ClassEntity>();
this.properties = new LinkedHashMap<String, PropertyEntity>();
this.staticProperties = new LinkedHashMap<String, PropertyEntity>();
this.constants = new LinkedHashMap<String, ConstantEntity>();
this.isInternal = false;
}
public ClassEntity(ClassWrapper wrapper) {
this((Context) null);
wrapper.onWrap(this);
}
public void setExtension(Extension extension) {
this.extension = extension;
}
public String getCompiledInternalName() {
return super.getInternalName();
}
@Override
public String getInternalName() {
/*if (isTrait()) { depricated check, todo remove.
throw new CriticalException("Disable of using internal names for traits, trait '" + getName() + "'");
} */
return super.getInternalName();
}
public boolean isStatic() {
return isStatic;
}
public void setStatic(boolean aStatic) {
isStatic = aStatic;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isInternal() {
return isInternal;
}
public boolean isTrait() {
return type == Type.TRAIT;
}
public void setInternal(boolean isInternal) {
this.isInternal = isInternal;
}
public boolean isNotRuntime() {
return isNotRuntime;
}
public void setNotRuntime(boolean isNotRuntime) {
this.isNotRuntime = isNotRuntime;
}
public void doneDeclare() {
if (isClass()) {
methodConstruct = methods.get("__construct");
if (methodConstruct != null
&& (methodConstruct.getPrototype() == null || !methodConstruct.getPrototype().isAbstractable()))
methodConstruct.setDynamicSignature(true);
methodDestruct = methods.get("__destruct");
methodMagicSet = methods.get("__set");
methodMagicGet = methods.get("__get");
methodMagicUnset = methods.get("__unset");
methodMagicIsset = methods.get("__isset");
methodMagicCall = methods.get("__call");
methodMagicCallStatic = methods.get("__callstatic");
methodMagicInvoke = methods.get("__invoke");
methodMagicToString = methods.get("__tostring");
methodMagicClone = methods.get("__clone");
methodMagicSleep = methods.get("__sleep");
methodMagicWakeup = methods.get("__wakeup");
methodMagicDebugInfo = methods.get("__debuginfo");
}
}
/*
ClassReader classReader;
if (data != null)
classReader = new ClassReader(data);
else {
try {
classReader = new ClassReader(nativeClazz.getName());
} catch (IOException e) {
throw new CriticalException(e);
}
}
ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0);
return cachedClassNode = classNode;
*/
public Extension getExtension() {
return extension;
}
public boolean isDeprecated() {
return false; // TODO
}
public boolean isAbstract() {
return isAbstract;
}
public void setAbstract(boolean anAbstract) {
isAbstract = anAbstract;
}
public boolean isFinal() {
return isFinal;
}
public void setFinal(boolean aFinal) {
isFinal = aFinal;
}
public Type getType() {
return type;
}
public boolean isInterface() {
return type == Type.INTERFACE;
}
public boolean isClass() {
return type == Type.CLASS;
}
public boolean isHiddenInCallStack() {
return false;
}
public void setType(Type type) {
this.type = type;
}
public Map<String, MethodEntity> getMethods() {
return methods;
}
public int getMethodCounts() {
return methodCounts;
}
public void __setMethodCounts(int methodCounts) {
this.methodCounts = methodCounts;
}
public List<MethodEntity> getOwnedMethods() {
List<MethodEntity> result = new ArrayList<MethodEntity>();
for (MethodEntity el : methods.values()) {
if (el.isOwned(this))
result.add(el);
}
return result;
}
public SignatureResult addMethod(MethodEntity method, String realName) {
String name = realName == null ? method.getLowerName() : realName;
SignatureResult addResult = new SignatureResult();
if (method.isAbstract && method.isFinal) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.FINAL_ABSTRACT, method));
} else if (method.isAbstractable() && !(method.isAbstract || type == Type.INTERFACE)) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.NON_ABSTRACT, method));
} else if (method.isAbstract && !method.isAbstractable()) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.NON_ABSTRACTABLE, method));
} else if (method.isAbstract && !(this.isAbstract || isTrait())) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.NON_EXISTS, method));
} else if (type == Type.INTERFACE &&
(method.modifier != Modifier.PUBLIC || method.isFinal)) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.INVALID_ACCESS_FOR_INTERFACE, method));
}
if (magicSignatureClass != null) {
MethodEntity systemMethod = magicSignatureClass.findMethod(name.toLowerCase());
if (systemMethod != null && method.clazz.getId() == getId()) {
if (method.prototype == null)
method.setPrototype(systemMethod);
if (systemMethod.getModifier() == Modifier.PUBLIC && method.getModifier() != Modifier.PUBLIC) {
addResult.add(InvalidMethod.warning(InvalidMethod.Kind.MAGIC_MUST_BE_PUBLIC, method));
//method.setModifier(Modifier.PUBLIC);
}
if (!systemMethod.equalsBySignature(method, false)) {
addResult.add(InvalidMethod.error(InvalidMethod.Kind.INVALID_SIGNATURE, method));
} else if (systemMethod.isStatic && !method.isStatic)
addResult.add(InvalidMethod.warning(InvalidMethod.Kind.MUST_STATIC, method));
else if (!systemMethod.isStatic && method.isStatic) {
//method.setStatic(false);
addResult.add(InvalidMethod.warning(InvalidMethod.Kind.MUST_NON_STATIC, method));
}
}
}
if (parent == null || (!name.equals(parent.lowerName) || !methods.containsKey(name)))
this.methods.put(name, method);
if (name.equals(lowerName) && !isTrait()) {
methods.put("__construct", method);
}
return addResult;
}
public int nextMethodIndex() {
return methodCounts++;
}
public MethodEntity findMethod(String name) {
return methods.get(name);
}
public ConstantEntity findConstant(String name) {
return constants.get(name);
}
// use can pass specific names
public PropertyEntity findProperty(String name) {
int pos = name.lastIndexOf('\0');
if (pos > -1 && pos + 1 < name.length())
name = name.substring(pos + 1);
return properties.get(name);
}
public PropertyEntity findStaticProperty(String name) {
return staticProperties.get(name);
}
public ClassEntity getParent() {
return parent;
}
public boolean isInstanceOf(Class<? extends IObject> clazz) {
return isInstanceOf(ReflectionUtils.getClassName(clazz));
}
public boolean isInstanceOf(ClassEntity what) {
return what != null && (id == what.id || instanceOfList.contains(what.lowerName));
}
public boolean isInstanceOf(String name) {
if (name == null)
throw new IllegalArgumentException();
String lowerName = name.toLowerCase();
return instanceOfList.contains(lowerName) || this.lowerName.equals(lowerName);
}
public boolean isInstanceOfLower(String lowerName) {
if (lowerName == null)
throw new IllegalArgumentException();
return instanceOfList.contains(lowerName) || this.lowerName.equals(lowerName);
}
public SignatureResult updateParentMethods() {
SignatureResult result = new SignatureResult();
if (parent != null) {
for (Map.Entry<String, MethodEntity> entry : parent.getMethods().entrySet()) {
MethodEntity implMethod = findMethod(entry.getKey());
MethodEntity method = entry.getValue();
if (implMethod == method) continue;
if (implMethod == null) {
SignatureResult addResult = addMethod(method, entry.getKey());
if (methodConstruct == null && !isTrait() && method.getName().equalsIgnoreCase(parent.getName())) {
if (!method.isAbstractable() && !methods.containsKey("__construct")) {
method.setDynamicSignature(true);
methods.put("__construct", method);
}
}
result.methods.addAll(addResult.methods);
} else {
implMethod.setPrototype(method);
if (method.isFinal)
result.add(InvalidMethod.error(InvalidMethod.Kind.FINAL, implMethod));
if (!isAbstract && method.isAbstract && implMethod.isAbstract)
result.add(InvalidMethod.error(InvalidMethod.Kind.NON_EXISTS, implMethod));
if (!implMethod.equalsBySignature(method)) {
if (!method.isDynamicSignature() || method.isAbstractable()) {
boolean isStrict = true;
MethodEntity pr = method;
while (pr != null) {
if (pr.isAbstractable()) {
isStrict = false;
break;
}
pr = method.getPrototype();
}
result.add(
!isStrict
? InvalidMethod.error(InvalidMethod.Kind.INVALID_SIGNATURE, implMethod)
: InvalidMethod.strict(InvalidMethod.Kind.INVALID_SIGNATURE, implMethod)
);
}
} else if (implMethod.isStatic() && !method.isStatic()) {
result.add(InvalidMethod.error(InvalidMethod.Kind.MUST_NON_STATIC, implMethod));
} else if (!implMethod.isStatic() && method.isStatic()) {
result.add(InvalidMethod.error(InvalidMethod.Kind.MUST_STATIC, implMethod));
}
if (method.isPublic() && !implMethod.isPublic())
result.add(InvalidMethod.error(InvalidMethod.Kind.MUST_BE_PUBLIC, implMethod));
else if (method.isProtected() && implMethod.isPrivate())
result.add(InvalidMethod.error(InvalidMethod.Kind.MUST_BE_PROTECTED, implMethod));
}
}
doneDeclare();
}
return result;
}
public ExtendsResult setParent(ClassEntity parent) {
return setParent(parent, true);
}
public ExtendsResult setParent(ClassEntity parent, boolean updateParentMethods) {
ExtendsResult result = new ExtendsResult(parent);
if (this.parent != null) {
throw new RuntimeException("Cannot re-assign parent for classes");
}
this.parent = parent;
if (parent != null) {
if (parent.useJavaLikeNames) {
this.useJavaLikeNames = true;
}
this.methodCounts = parent.methodCounts;
this.instanceOfList.add(parent.getLowerName());
this.instanceOfList.addAll(parent.instanceOfList);
this.interfaces.putAll(parent.interfaces);
this.properties.putAll(parent.properties);
this.staticProperties.putAll(parent.staticProperties);
this.constants.putAll(parent.constants);
}
if (updateParentMethods)
result.methods = updateParentMethods();
return result;
}
/**
* return empty list if success else list with not valid implemented methods in class
*
* @param _interface
* @return
*/
public ImplementsResult addInterface(ClassEntity _interface) {
ImplementsResult result = new ImplementsResult(_interface);
for (ConstantEntity e : _interface.constants.values()) {
ConstantEntity origin = constants.get(e.getName());
if (origin != null && e.getClazz().getId() == _interface.getId() &&
(parent == null || parent.constants.get(e.getName()) == null))
result.signature.addConstant(InvalidConstant.error(origin, e));
}
this.constants.putAll(_interface.constants);
this.interfaces.put(_interface.getLowerName(), _interface);
this.instanceOfList.add(_interface.getLowerName());
this.instanceOfList.addAll(_interface.instanceOfList);
for (MethodEntity method : _interface.getMethods().values()) {
MethodEntity implMethod = findMethod(method.getLowerName());
if (implMethod == method) continue;
if (implMethod == null) {
addMethod(method, null);
if (type == Type.CLASS && !isAbstract)
result.signature.add(InvalidMethod.error(InvalidMethod.Kind.NON_EXISTS, method));
} else {
implMethod.setPrototype(method);
if (/*!method.isDynamicSignature() &&*/ !implMethod.equalsBySignature(method)) { // checking dynamic for only extends
result.signature.add(InvalidMethod.error(InvalidMethod.Kind.INVALID_SIGNATURE, implMethod));
} else if (implMethod.isStatic() && !method.isStatic()) {
result.signature.add(InvalidMethod.error(InvalidMethod.Kind.MUST_NON_STATIC, implMethod));
} else if (!implMethod.isStatic() && method.isStatic()) {
result.signature.add(InvalidMethod.error(InvalidMethod.Kind.MUST_STATIC, implMethod));
}
}
}
return result;
}
public Map<String, ClassEntity> getInterfaces() {
return interfaces;
}
public void addTrait(ClassEntity trait) {
if (!trait.isTrait())
throw new IllegalArgumentException("'" + trait.getName() + "' is not a trait");
this.traits.put(trait.getLowerName(), trait);
}
public boolean hasTrait(String traitLowerName) {
return this.traits.containsKey(traitLowerName);
}
public Map<String, ClassEntity> getTraits() {
return traits;
}
public Collection<ConstantEntity> getConstants() {
return constants.values();
}
public Collection<PropertyEntity> getProperties() {
return properties.values();
}
public Collection<PropertyEntity> getStaticProperties() {
return staticProperties.values();
}
public void addConstant(ConstantEntity constant) {
constants.put(constant.getName(), constant);
constant.setClazz(this);
}
public void addDynamicConstant(Environment env, String name, Memory value) {
ConstantEntity entity = constants.get(name);
env.getOrCreateStatic(entity.getInternalName(), value);
}
public void addDynamicStaticProperty(Environment env, String name, Memory value) {
PropertyEntity prop = staticProperties.get(name);
env.getOrCreateStatic(prop.specificName, value);
}
public void addDynamicProperty(Environment env, String name, Memory value) {
PropertyEntity prop = properties.get(name);
env.getOrCreateStatic(prop.getInternalName(), value);
}
public PropertyResult addProperty(PropertyEntity property) {
PropertyResult result = new PropertyResult();
PropertyEntity prototype = null;
if (property.isStatic()) {
prototype = staticProperties.get(property.getLowerName());
if (prototype != null && prototype.getModifier() != property.getModifier()) {
property.setPrototype(prototype);
}
if (prototype == null)
prototype = properties.get(property.getName());
staticProperties.put(property.getName(), property);
} else {
prototype = properties.get(property.getLowerName());
if (prototype != null && prototype.getModifier() != property.getModifier()) {
property.setPrototype(prototype);
}
if (prototype == null)
prototype = staticProperties.get(property.getName());
properties.put(property.getName(), property);
}
if (prototype != null) {
if (property.getPrototype() != null) {
if (prototype.isProtected() && property.isPrivate()) {
result.addError(InvalidProperty.Kind.MUST_BE_PROTECTED, property);
}
if (prototype.isPublic() && !property.isPublic()) {
result.addError(InvalidProperty.Kind.MUST_BE_PUBLIC, property);
}
}
boolean overridden = property.modifier.ordinal() > prototype.modifier.ordinal();
if (!property.isPrivate() && property.modifier == prototype.modifier)
overridden = true;
if (property.isPublic() && prototype.isProtected())
overridden = true;
if (prototype.isStatic() && !property.isStatic()) {
if (overridden) {
property.setPrototype(prototype);
result.addError(InvalidProperty.Kind.STATIC_AS_NON_STATIC, property);
}
} else if (property.isStatic() && !prototype.isStatic()) {
if (overridden) {
property.setPrototype(prototype);
result.addError(InvalidProperty.Kind.NON_STATIC_AS_STATIC, property);
}
}
}
property.setClazz(this);
return result;
}
protected void addStaticProperty(PropertyEntity property) {
if (!property.isStatic())
throw new IllegalArgumentException("Property must be static");
staticProperties.put(property.getLowerName(), property);
property.setClazz(this);
}
public MethodEntity getConstructor() {
return constructor;
}
public void setConstructor(MethodEntity constructor) {
this.constructor = constructor;
constructor.setClazz(this);
}
public DocumentComment getDocComment() {
return docComment;
}
public void setDocComment(DocumentComment docComment) {
this.docComment = docComment;
}
public Class<?> getNativeClass() {
return nativeClazz;
}
@Deprecated
public Class<?> getNativeClazz() {
return nativeClazz;
}
protected static void invalidAccessToProperty(Environment env, TraceInfo trace, PropertyEntity entity, int accessFlag) {
switch (accessFlag) {
case 1:
env.error(trace, ErrorType.E_ERROR,
Messages.ERR_ACCESS_TO_PROTECTED_PROPERTY.fetch(
entity.getClazz().getName(), entity.getName()
));
case 2:
env.error(trace, ErrorType.E_ERROR,
Messages.ERR_ACCESS_TO_PRIVATE_PROPERTY.fetch(
entity.getClazz().getName(), entity.getName()
));
}
}
public void setNativeClazz(Class<?> nativeClazz) {
this.nativeClazz = nativeClazz;
if (nativeClazz.getAnnotation(Reflection.UseJavaLikeNames.class) != null) {
useJavaLikeNames = true;
}
if (!nativeClazz.isInterface()) {
try {
this.nativeConstructor = nativeClazz.getConstructor(Environment.class, ClassEntity.class);
this.nativeConstructor.setAccessible(true);
} catch (NoSuchMethodException e) {
this.nativeConstructor = null;
//if (IObject.class.isAssignableFrom(getNativeClass().getClass()))
// throw new CriticalException(e);
}
if (!this.isInternal) {
try {
if (isTrait()) {
this.nativeInitEnvironment = nativeClazz.getDeclaredMethod(
"__$initEnvironment", Environment.class, String.class
);
} else {
this.nativeInitEnvironment = nativeClazz.getDeclaredMethod(
"__$initEnvironment", Environment.class
);
}
this.nativeInitEnvironment.setAccessible(true);
} catch (NoSuchMethodException e) {
this.nativeInitEnvironment = null;
}
}
}
}
public ModuleEntity getModule() {
return module;
}
public void setModule(ModuleEntity module) {
this.module = module;
}
public void initEnvironment(Environment env) {
if (isClass() && nativeInitEnvironment != null) {
try {
nativeInitEnvironment.invoke(null, env);
} catch (InvocationTargetException e) {
env.__throwException(e);
} catch (IllegalAccessException e) {
throw new CriticalException(e);
}
}
if (!traits.isEmpty()) {
Set<ClassEntity> used = new HashSet<ClassEntity>();
try {
for (ClassEntity trait : traits.values()) {
trait.initTraitEnvironment(env, this, used);
}
} catch (Exception e) {
env.catchUncaught(e);
} catch (Throwable e) {
throw new CriticalException(e);
}
}
}
protected void initTraitEnvironment(Environment env, ClassEntity originClass, Set<ClassEntity> used) throws Throwable {
if (nativeInitEnvironment != null) {
try {
nativeInitEnvironment.invoke(null, env, originClass.getName());
} catch (InvocationTargetException e) {
env.__throwException(e);
}
}
for (ClassEntity trait : traits.values()) {
if (used.add(trait)) {
trait.initTraitEnvironment(env, originClass, used);
}
}
}
public <T extends IObject> T newObjectWithoutConstruct(Environment env) {
IObject object = null;
try {
if (nativeConstructor != null)
object = (IObject) nativeConstructor.newInstance(env, this);
} catch (InvocationTargetException e) {
env.__throwException(e);
return null;
} catch (InstantiationException e) {
throw new CriticalException(e);
} catch (IllegalAccessException e) {
throw new CriticalException(e);
}
return (T) object;
}
public <T extends IObject> T newMock(Environment env) throws Throwable {
if (nativeConstructor == null) {
env.error(env.trace(), ErrorType.E_CORE_ERROR, "Cannot find a java constructor %s(Environment, ClassEntity)", getName());
}
try {
IObject object = (IObject) nativeConstructor.newInstance(env, this);
object.setAsMock();
return (T) object;
} catch (InstantiationException e) {
return null;
}
}
public <T extends IObject> T newObject(Environment env, TraceInfo trace, boolean doConstruct, Memory... args)
throws Throwable {
if (isAbstract) {
env.error(trace, "Cannot instantiate abstract class %s", name);
} else if (type == Type.INTERFACE) {
env.error(trace, "Cannot instantiate interface %s", name);
} else if (type == Type.TRAIT) {
env.error(trace, "Cannot instantiate trait %s", name);
}
IObject object;
try {
if (nativeConstructor == null) {
env.error(trace, ErrorType.E_CORE_ERROR, "Cannot find a java constructor %s(Environment, ClassEntity)", getName());
}
object = (IObject) nativeConstructor.newInstance(env, this);
} catch (InvocationTargetException e) {
env.__throwException(e);
return null;
}
ArrayMemory props = object.getProperties();
for (PropertyEntity property : getProperties()) {
if (id == property.clazz.getId() && property.getGetter() == null) {
props.putAsKeyString(
property.getSpecificName(),
property.getDefaultValue(env).toImmutable()
);
}
}
ClassEntity tmp = parent;
while (tmp != null) {
long otherId = tmp.getId();
for (PropertyEntity property : tmp.getProperties()) {
if (property.getClazz().getId() == otherId && property.getGetter() == null) {
if (property.modifier != Modifier.PROTECTED || props.getByScalar(property.getName()) == null)
props.getByScalarOrCreate(
property.getSpecificName(),
property.getDefaultValue(env).toImmutable()
);
}
}
tmp = tmp.parent;
}
if (doConstruct && methodConstruct != null) {
ObjectInvokeHelper.invokeMethod(object, methodConstruct, env, trace, args, true);
}
return (T) object;
}
public <T extends IObject> T cloneObject(T value, Environment env, TraceInfo trace) throws Throwable {
IObject copy = this.newObjectWithoutConstruct(env);
ForeachIterator iterator = value.getProperties().foreachIterator(false, false);
ArrayMemory props = copy.getProperties();
while (iterator.next()) {
Object key = iterator.getKey();
if (key instanceof String) {
String name = (String) key;
if (name.indexOf('\0') > -1)
name = name.substring(name.lastIndexOf('\0') + 1);
PropertyEntity entity = properties.get(name);
if (entity != null) {
if (props.getByScalar(entity.getSpecificName()) == null)
props.put(entity.getSpecificName(), iterator.getValue().toImmutable());
} else
props.put(key, iterator.getValue().toImmutable());
} else
props.put(key, iterator.getValue().toImmutable());
}
if (methodMagicClone != null) {
ObjectInvokeHelper.invokeMethod(copy, methodMagicClone, env, trace, null, true);
}
return (T) copy;
}
public Memory concatProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return new StringMemory(o1.concat(o2));
}
}, callCache, cacheIndex);
}
public Memory plusProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, final ReferenceMemory oldValue)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
if (oldValue != null)
oldValue.assign(o1);
return o1.plus(o2);
}
}, null, 0);
}
public Memory minusProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, final ReferenceMemory oldValue)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
if (oldValue != null)
oldValue.assign(o1);
return o1.minus(o2);
}
}, null, 0);
}
public Memory mulProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.mul(o2);
}
}, callCache, cacheIndex);
}
public Memory divProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.div(o2);
}
}, callCache, cacheIndex);
}
public Memory modProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.mod(o2);
}
}, callCache, cacheIndex);
}
public Memory bitAndProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.bitAnd(o2);
}
}, callCache, cacheIndex);
}
public Memory bitOrProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.bitOr(o2);
}
}, callCache, cacheIndex);
}
public Memory bitXorProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.bitXor(o2);
}
}, callCache, cacheIndex);
}
public Memory bitShrProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.bitShr(o2);
}
}, callCache, cacheIndex);
}
public Memory bitShlProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
return setProperty(env, trace, object, property, memory, new SetterCallback() {
@Override
public Memory invoke(Memory o1, Memory o2) {
return o1.bitShl(o2);
}
}, callCache, cacheIndex);
}
public void appendProperty(IObject object, String property, Memory value) {
object.getProperties().put(property, value);
}
public Memory refOfProperty(ArrayMemory props, String name) {
PropertyEntity entity = properties.get(name);
return props.refOfIndex(entity == null ? name : entity.getSpecificName());
}
public Memory setProperty(Environment env, TraceInfo trace,
IObject object, String property, Memory memory, SetterCallback callback,
PropertyCallCache callCache, int cacheIndex)
throws Throwable {
ReferenceMemory value;
PropertyEntity entity = callCache == null ? null : callCache.get(env, cacheIndex);
if (entity == null) {
ClassEntity context = env.getLastClassOnStack();
entity = isInstanceOf(context) ? context.properties.get(property) : properties.get(property);
if (callCache != null && entity != null) {
callCache.put(env, cacheIndex, entity);
}
}
if (entity == null) {
PropertyEntity staticEntity = staticProperties.get(property);
if (staticEntity != null) {
invalidAccessToProperty(env, trace, staticEntity, staticEntity.canAccess(env));
env.error(trace, ErrorType.E_STRICT,
Messages.ERR_ACCESSING_STATIC_PROPERTY_AS_NON_STATIC,
staticEntity.getClazz().getName(),
staticEntity.getName()
);
}
}
int accessFlag = entity == null ? 0 : entity.canAccess(env);
ArrayMemory props = object.getProperties();
if (entity != null) {
if (entity.setter != null) {
if (callback != null)
memory = callback.invoke(getProperty(env, trace, object, property, null, 0), memory);
try {
ObjectInvokeHelper.invokeMethod(object, entity.setter, env, trace, new Memory[]{memory}, false);
} catch (IllegalArgumentException e) {
if (!object.getReflection().isInstanceOf(entity.setter.getClazz())) {
return setProperty(env, trace, object, property, memory, callback, null, 0);
}
throw e;
}
return memory;
} else if (entity.getter != null) {
env.error(trace, ErrorType.E_RECOVERABLE_ERROR, Messages.ERR_READONLY_PROPERTY.fetch(entity.getClazz().getName(), property));
}
}
value = props == null || accessFlag != 0 ? null : props.getByScalar(entity == null ? property : entity.specificName);
if (value == null) {
boolean recursive = false;
ClassEntity context = env.getLastClassOnStack();
if (context != null && methodMagicSet != null && context.getId() == methodMagicSet.getClazz().getId()) {
recursive = env.peekCall(0).flags == FLAG_SET;
}
if (methodMagicSet != null && !recursive) {
StringMemory memoryProperty = new StringMemory(property);
if (callback != null) {
Memory o1 = Memory.NULL;
if (methodMagicGet != null) {
try {
Memory[] args = new Memory[]{memoryProperty};
env.pushCall(
trace, object, args, methodMagicGet.getName(), methodMagicSet.getClazz().getName(), name
);
env.peekCall(0).flags = FLAG_GET;
InvokeArgumentHelper.checkType(env, trace, methodMagicGet, args);
o1 = methodMagicGet.invokeDynamic(object, env, memoryProperty);
} finally {
env.popCall();
}
}
memory = callback.invoke(o1, memory);
}
try {
Memory[] args = new Memory[]{memoryProperty, memory};
env.pushCall(trace, object, args, methodMagicSet.getName(), methodMagicSet.getClazz().getName(), name);
env.peekCall(0).flags = FLAG_SET;
InvokeArgumentHelper.checkType(env, trace, methodMagicSet, args);
methodMagicSet.invokeDynamic(object, env, args);
} finally {
env.popCall();
}
} else {
/*if (accessFlag != 0) {
invalidAccessToProperty(env, trace, entity, accessFlag);
return Memory.NULL;
}*/
if (callback != null)
memory = callback.invoke(Memory.NULL, memory);
String name = property;
if (entity != null) {
if (accessFlag != 0 && context == null) {
switch (accessFlag) {
case 2:
if (object.getReflection().getId() == entity.getClazz().getId()) {
invalidAccessToProperty(env, trace, entity, accessFlag);
return Memory.NULL;
}
break;
case 1:
invalidAccessToProperty(env, trace, entity, accessFlag);
return Memory.NULL;
}
}
if (context != null) {
switch (entity.modifier) {
case PRIVATE:
if (entity.getClazz().getId() == context.getId())
name = entity.specificName;
break;
case PROTECTED:
if (context.isInstanceOf(entity.getClazz()))
name = entity.specificName;
}
}
}
return props == null
? Memory.NULL
: (entity == null ? props.refOfIndex(name).assign(memory) : entity.assignValue(env, trace, object, name, memory));
}
} else {
if (callback != null)
memory = callback.invoke(value, memory);
if (entity instanceof CompilePropertyEntity) {
return entity.assignValue(env, trace, object, property, memory);
}
return value.assign(memory);
}
return memory;
}
public Memory unsetProperty(Environment env, TraceInfo trace, IObject object, String property,
PropertyCallCache callCache, int index)
throws Throwable {
ClassEntity context = env.getLastClassOnStack();
PropertyEntity entity = isInstanceOf(context) ? context.properties.get(property) : properties.get(property);
int accessFlag = entity == null ? 0 : entity.canAccess(env);
if (entity == null) {
PropertyEntity staticEntity = staticProperties.get(property);
if (staticEntity != null) {
invalidAccessToProperty(env, trace, staticEntity, staticEntity.canAccess(env));
}
}
ArrayMemory props = object.getProperties();
if (props == null
|| accessFlag != 0
|| props.removeByScalar(entity == null ? property : entity.specificName) == null) {
if (methodMagicUnset != null) {
if (context != null && context.getId() == methodMagicUnset.getClazz().getId()) {
if (env.peekCall(0).flags == FLAG_UNSET) {
return Memory.NULL;
}
}
try {
Memory[] args = new Memory[]{new StringMemory(property)};
env.pushCall(trace, object, args, methodMagicUnset.getName(), methodMagicUnset.getClazz().getName(), name);
env.peekCall(0).flags = FLAG_UNSET;
InvokeArgumentHelper.checkType(env, trace, methodMagicUnset, args);
methodMagicUnset.invokeDynamic(object, env, args);
} finally {
env.popCall();
}
return Memory.NULL;
}
}
if (accessFlag != 0)
invalidAccessToProperty(env, trace, entity, accessFlag);
entity = staticProperties.get(property);
if (entity != null) {
env.error(trace, ErrorType.E_STRICT,
Messages.ERR_ACCESSING_STATIC_PROPERTY_AS_NON_STATIC,
entity.getClazz().getName(),
entity.getName()
);
}
return Memory.NULL;
}
public Memory emptyProperty(Environment env, TraceInfo trace, IObject object, String property)
throws Throwable {
ClassEntity contex = env.getLastClassOnStack();
PropertyEntity entity = isInstanceOf(contex) ? contex.properties.get(property) : properties.get(property);
int accessFlag = entity == null ? 0 : entity.canAccess(env);
ArrayMemory props = object.getProperties();
if (props != null && accessFlag == 0) {
Memory tmp = props.getByScalar(entity == null ? property : entity.specificName);
if (tmp != null) {
return tmp.toBoolean() ? Memory.TRUE : Memory.NULL;
} else
return Memory.NULL;
}
if (methodMagicIsset != null) {
Memory result;
if (contex != null && contex.getId() == methodMagicIsset.getClazz().getId()) {
if (env.peekCall(0).flags == FLAG_ISSET) {
return object.getProperties().getByScalar(property) != null ? Memory.TRUE : Memory.NULL;
}
}
try {
Memory[] args = new Memory[]{new StringMemory(property)};
env.pushCall(
trace, object, args, methodMagicIsset.getName(), methodMagicIsset.getClazz().getName(), name
);
env.peekCall(0).flags = FLAG_ISSET;
InvokeArgumentHelper.checkType(env, trace, methodMagicIsset, new StringMemory(property));
result = methodMagicIsset.invokeDynamic(object, env, new StringMemory(property))
.toBoolean() ? Memory.TRUE : Memory.NULL;
} finally {
env.popCall();
}
return result;
}
return Memory.NULL;
}
public Memory issetProperty(Environment env, TraceInfo trace, IObject object, String property,
PropertyCallCache callCache, int cacheIndex)
throws Throwable {
PropertyEntity entity = callCache == null || env instanceof ConcurrentEnvironment ? null : callCache.get(env, cacheIndex);
if (entity == null) {
ClassEntity contex = env.getLastClassOnStack();
entity = isInstanceOf(contex) ? contex.properties.get(property) : properties.get(property);
if (entity != null && callCache != null) {
callCache.put(env, cacheIndex, entity);
}
}
int accessFlag = entity == null ? 0 : entity.canAccess(env);
ArrayMemory props = object.getProperties();
Memory tmp = props == null || accessFlag != 0
? null
: props.getByScalar(entity == null ? property : entity.specificName);
if (tmp != null)
return tmp.isNull() ? tmp : Memory.TRUE;
if (methodMagicIsset != null) {
Memory result;
ClassEntity contex = env.getLastClassOnStack();
if (contex != null && contex.getId() == methodMagicIsset.getClazz().getId())
if (env.peekCall(0).flags == FLAG_ISSET) {
return object.getProperties().getByScalar(property) != null ? Memory.TRUE : Memory.NULL;
}
try {
Memory[] args = new Memory[]{new StringMemory(property)};
env.pushCall(trace, object, args, methodMagicIsset.getName(), methodMagicIsset.getClazz().getName(), name);
env.peekCall(0).flags = FLAG_ISSET;
InvokeArgumentHelper.checkType(env, trace, methodMagicIsset, new StringMemory(property));
result = methodMagicIsset.invokeDynamic(object, env, new StringMemory(property))
.toBoolean() ? Memory.TRUE : Memory.NULL;
} finally {
env.popCall();
}
return result;
}
return Memory.NULL;
}
public Memory getStaticProperty(Environment env, TraceInfo trace, String property, boolean errorIfNotExists,
boolean checkAccess, ClassEntity context, PropertyCallCache callCache, int cacheIndex)
throws Throwable {
PropertyEntity entity = callCache == null || env instanceof ConcurrentEnvironment || context != null ? null : callCache.get(env, cacheIndex);
if (entity == null) {
boolean saveCache = context == null && callCache != null;
context = context == null ? env.getLastClassOnStack() : context;
entity = isInstanceOf(context) ? context.findStaticProperty(property) : findStaticProperty(property);
if (saveCache && entity != null) {
callCache.put(env, cacheIndex, entity);
}
}
if (entity == null) {
if (errorIfNotExists)
env.error(trace, Messages.ERR_ACCESS_TO_UNDECLARED_STATIC_PROPERTY.fetch(name, property));
return Memory.NULL;
}
if (checkAccess) {
int accessFlag = entity.canAccess(env, context);
if (accessFlag != 0) {
invalidAccessToProperty(env, trace, entity, accessFlag);
return Memory.NULL;
}
}
return entity.getStaticValue(env, trace);
}
public Memory getRefProperty(Environment env, TraceInfo trace, IObject object, String property,
PropertyCallCache callCache, int cacheIndex)
throws Throwable {
Memory value;
PropertyEntity entity = callCache == null || env instanceof ConcurrentEnvironment ? null : callCache.get(env, cacheIndex);
if (entity == null) {
ClassEntity context = env.getLastClassOnStack();
entity = isInstanceOf(context) ? context.properties.get(property) : properties.get(property);
if (callCache != null) {
callCache.put(env, cacheIndex, entity);
}
}
if (entity == null) {
PropertyEntity staticEntity = staticProperties.get(property);
if (staticEntity != null) {
invalidAccessToProperty(env, trace, staticEntity, staticEntity.canAccess(env));
env.error(trace, ErrorType.E_STRICT,
Messages.ERR_ACCESSING_STATIC_PROPERTY_AS_NON_STATIC,
staticEntity.getClazz().getName(),
staticEntity.getName()
);
}
}
int accessFlag = entity == null ? 0 : entity.canAccess(env);
ArrayMemory props = object.getProperties();
value = props == null || accessFlag != 0 ? null : props.getByScalar(entity == null ? property : entity.specificName);
if (accessFlag != 0)
invalidAccessToProperty(env, trace, entity, accessFlag);
if (value == null) {
value = props == null ? new ReferenceMemory() : object.getProperties().refOfIndex(property);
if (methodMagicGet != null || methodMagicSet != null) {
env.error(trace,
props == null ? ErrorType.E_ERROR : ErrorType.E_NOTICE,
Messages.ERR_INDIRECT_MODIFICATION_OVERLOADED_PROPERTY, name, property);
}
}
return value;
}
public Memory getProperty(Environment env, TraceInfo trace, IObject object, String property,
PropertyCallCache callCache, int cacheIndex)
throws Throwable {
Memory value;
PropertyEntity entity = callCache == null || env instanceof ConcurrentEnvironment ? null : callCache.get(env, cacheIndex);
if (entity == null) {
ClassEntity context = env.getLastClassOnStack();
entity = isInstanceOf(context) ? context.properties.get(property) : properties.get(property);
if (callCache != null && entity != null) {
callCache.put(env, cacheIndex, entity);
}
}
if (entity == null) {
PropertyEntity staticEntity = staticProperties.get(property);
if (staticEntity != null) {
invalidAccessToProperty(env, trace, staticEntity, staticEntity.canAccess(env));
env.error(trace, ErrorType.E_STRICT,
Messages.ERR_ACCESSING_STATIC_PROPERTY_AS_NON_STATIC,
staticEntity.getClazz().getName(),
staticEntity.getName()
);
}
}
int accessFlag = entity == null ? 0 : entity.canAccess(env);
if (entity != null && accessFlag != 0) {
value = null;
} else {
if (entity != null) {
value = entity.getValue(env, trace, object);
} else {
ArrayMemory props = object.getProperties();
value = props == null ? null : props.getByScalar(property);
}
}
if (value != null)
return value;
if (methodMagicGet != null) {
Memory result;
ClassEntity context = env.getLastClassOnStack();
if (context != null && context.getId() == methodMagicGet.getClazz().getId()) {
if (env.peekCall(0).flags == FLAG_GET) {
env.error(trace, ErrorType.E_NOTICE, Messages.ERR_UNDEFINED_PROPERTY, name, property);
return Memory.NULL;
}
}
try {
Memory[] args = new Memory[]{new StringMemory(property)};
env.pushCall(trace, object, args, methodMagicGet.getName(), methodMagicGet.getClazz().getName(), name);
env.peekCall(0).flags = FLAG_GET;
InvokeArgumentHelper.checkType(env, trace, methodMagicGet, args);
result = methodMagicGet.invokeDynamic(object, env, args);
} finally {
env.popCall();
}
return result;
}
if (accessFlag != 0)
invalidAccessToProperty(env, trace, entity, accessFlag);
env.error(trace, ErrorType.E_NOTICE, Messages.ERR_UNDEFINED_PROPERTY, name, property);
return Memory.NULL;
}
public void setProperty(IObject object, String name, Memory value) {
PropertyEntity prop = findProperty(name);
if (prop == null)
throw new RuntimeException("Property '" + name + "' not found");
object.getProperties().put(prop.specificName, value == null ? Memory.NULL : value);
}
private static interface SetterCallback {
Memory invoke(Memory o1, Memory o2);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassEntity)) return false;
if (!super.equals(o)) return false;
ClassEntity entity = (ClassEntity) o;
if (id != entity.id) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (int) (id ^ (id >>> 32));
return result;
}
public static class InvalidConstant {
public final ConstantEntity constant;
public final ConstantEntity prototype;
public final ErrorType errorType;
protected InvalidConstant(ConstantEntity constant, ConstantEntity prototype, ErrorType errorType) {
this.constant = constant;
this.errorType = errorType;
this.prototype = prototype;
}
public static InvalidConstant error(ConstantEntity constant, ConstantEntity prototype) {
return new InvalidConstant(constant, prototype, ErrorType.E_ERROR);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InvalidConstant)) return false;
InvalidConstant that = (InvalidConstant) o;
return constant.equals(that.constant);
}
@Override
public int hashCode() {
return constant.hashCode();
}
}
public static class InvalidMethod {
public enum Kind {
NON_EXISTS, INVALID_SIGNATURE, MUST_STATIC, MUST_NON_STATIC, MAGIC_MUST_BE_PUBLIC, MUST_BE_PUBLIC, MUST_BE_PROTECTED,
FINAL, NON_ABSTRACT, NON_ABSTRACTABLE, INVALID_ACCESS_FOR_INTERFACE, FINAL_ABSTRACT, OVERRIDE_CONSTANTS
}
public final Kind kind;
public final MethodEntity method;
public final ErrorType errorType;
protected InvalidMethod(Kind kind, MethodEntity method, ErrorType errorType) {
this.kind = kind;
this.method = method;
this.errorType = errorType;
}
public static InvalidMethod warning(Kind kind, MethodEntity method) {
return new InvalidMethod(kind, method, ErrorType.E_WARNING);
}
public static InvalidMethod error(Kind kind, MethodEntity method) {
return new InvalidMethod(kind, method, ErrorType.E_ERROR);
}
public static InvalidMethod strict(Kind kind, MethodEntity method) {
return new InvalidMethod(kind, method, ErrorType.E_STRICT);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InvalidMethod)) return false;
InvalidMethod that = (InvalidMethod) o;
return method.equals(that.method) && kind.equals(that.kind);
}
@Override
public int hashCode() {
int result = kind.hashCode();
result = 31 * result + method.hashCode();
return result;
}
}
public static class InvalidProperty {
public enum Kind {MUST_BE_PROTECTED, MUST_BE_PUBLIC, STATIC_AS_NON_STATIC, NON_STATIC_AS_STATIC}
public final Kind kind;
public final PropertyEntity property;
public final ErrorType errorType;
protected InvalidProperty(Kind kind, PropertyEntity property, ErrorType errorType) {
this.kind = kind;
this.property = property;
this.errorType = errorType;
}
public static InvalidProperty warning(Kind kind, PropertyEntity property) {
return new InvalidProperty(kind, property, ErrorType.E_WARNING);
}
public static InvalidProperty error(Kind kind, PropertyEntity property) {
return new InvalidProperty(kind, property, ErrorType.E_ERROR);
}
public static InvalidProperty strict(Kind kind, PropertyEntity property) {
return new InvalidProperty(kind, property, ErrorType.E_STRICT);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InvalidProperty)) return false;
InvalidProperty that = (InvalidProperty) o;
return property.equals(that.property) && kind.equals(that.kind);
}
@Override
public int hashCode() {
int result = kind.hashCode();
result = 31 * result + property.hashCode();
return result;
}
}
public class PropertyResult {
private final Set<InvalidProperty> properties;
public PropertyResult() {
this.properties = new HashSet<InvalidProperty>();
}
public void add(InvalidProperty prop) {
this.properties.add(prop);
}
public void addError(InvalidProperty.Kind kind, PropertyEntity prop) {
add(InvalidProperty.error(kind, prop));
}
public void check(Environment env) {
for (InvalidProperty el : properties) {
ErrorException e = null;
switch (el.kind) {
case MUST_BE_PROTECTED:
e = new FatalException(
Messages.ERR_ACCESS_LEVEL_MUST_BE_PROTECTED_OR_WEAKER.fetch(
el.property.clazz.getName(),
el.property.getName(),
el.property.getPrototype().getClazz().getName()
),
el.property.getTrace()
);
break;
case MUST_BE_PUBLIC:
e = new FatalException(
Messages.ERR_ACCESS_LEVEL_MUST_BE_PUBLIC.fetch(
el.property.clazz.getName(),
el.property.getName(),
el.property.getPrototype().getClazz().getName()
),
el.property.getTrace()
);
break;
case STATIC_AS_NON_STATIC:
e = new FatalException(
Messages.ERR_CANNOT_REDECLARE_STATIC_AS_NON_STATIC.fetch(
el.property.getPrototype().getClazz().getName(),
el.property.getPrototype().getName(),
el.property.clazz.getName(),
el.property.getName()
),
el.property.getTrace()
);
break;
case NON_STATIC_AS_STATIC:
e = new FatalException(
Messages.ERR_CANNOT_REDECLARE_NON_STATIC_AS_STATIC.fetch(
el.property.getPrototype().getClazz().getName(),
el.property.getPrototype().getName(),
el.property.clazz.getName(),
el.property.getName()
),
el.property.getTrace()
);
break;
}
if (e != null)
if (env == null)
throw e;
else
env.error(e.getTraceInfo(), el.errorType, e.getMessage());
}
}
}
public class ImplementsResult {
ClassEntity parent;
SignatureResult signature;
ImplementsResult(ClassEntity parent) {
this.parent = parent;
this.signature = new SignatureResult();
}
public void check(Environment env) {
if (parent != null) {
if (parent.isNotRuntime && !isInternal()) {
FatalException e = new FatalException(
Messages.ERR_CANNOT_USE_SYSTEM_CLASS.fetch(parent.getName(), getName()),
ClassEntity.this.trace
);
if (env != null)
env.error(e.getTraceInfo(), e.getType(), e.getMessage());
else
throw e;
}
}
if (signature != null)
signature.check(env);
}
}
public class ExtendsResult {
ClassEntity parent;
SignatureResult methods;
ExtendsResult(ClassEntity parent) {
this.parent = parent;
this.methods = new SignatureResult();
}
public void check(Environment env) {
if (parent != null) {
if (parent.isNotRuntime && !isInternal()) {
FatalException e = new FatalException(
Messages.ERR_CANNOT_USE_SYSTEM_CLASS.fetch(parent.getName(), getName()),
ClassEntity.this.trace
);
if (env != null)
env.error(e.getTraceInfo(), e.getType(), e.getMessage());
else
throw e;
}
if (parent.isFinal) {
FatalException e = new FatalException(
Messages.ERR_CLASS_MAY_NOT_INHERIT_FINAL_CLASS.fetch(ClassEntity.this.getName(), parent.getName()),
ClassEntity.this.trace
);
if (env != null)
env.error(e.getTraceInfo(), e.getType(), e.getMessage());
else
throw e;
}
if (ClassEntity.this.type == Type.CLASS && parent.type != Type.CLASS) {
FatalException e = new FatalException(
Messages.ERR_CANNOT_EXTENDS.fetch(ClassEntity.this.getName(), parent.getName()),
ClassEntity.this.trace
);
if (env != null)
env.error(e.getTraceInfo(), e.getType(), e.getMessage());
else
throw e;
}
}
if (methods != null)
methods.check(env);
}
}
public class SignatureResult {
private final Set<InvalidMethod> methods;
private Set<InvalidConstant> overrideConstants_;
SignatureResult() {
methods = new HashSet<InvalidMethod>();
}
public void add(InvalidMethod el) {
methods.add(el);
}
public void addConstant(InvalidConstant el) {
if (overrideConstants_ == null)
overrideConstants_ = new HashSet<InvalidConstant>();
overrideConstants_.add(el);
}
public void check() {
check(null);
}
private ErrorException getException(Environment env, InvalidMethod el, Messages.Item message) {
return getException(env, el, message, false);
}
private ErrorException getException(Environment env, InvalidMethod el, Messages.Item message, boolean prototype) {
ErrorException e;
if (prototype) {
e = new FatalException(
message.fetch(
el.method.getPrototype().getSignatureString(false),
el.method.getSignatureString(false)
),
el.method.getTrace()
);
} else {
e = new FatalException(
message.fetch(el.method.getSignatureString(false)),
el.method.getTrace()
);
}
return e;
}
public void check(Environment env) {
if (overrideConstants_ != null)
for (InvalidConstant e : this.overrideConstants_) {
if (env != null)
env.error(
getTrace(), e.errorType,
Messages.ERR_CANNOT_INHERIT_OVERRIDE_CONSTANT,
e.constant.getName(),
e.prototype.getClazz().getName()
);
}
Set<InvalidMethod> nonExists = null;
for (InvalidMethod el : methods) {
ErrorException e = null;
switch (el.kind) {
case INVALID_SIGNATURE:
MethodEntity prototype = el.method.getPrototype();
if (!prototype.isAbstractable()) {
while (prototype.prototype != null && prototype.prototype.isAbstractable())
prototype = prototype.prototype;
}
e = new FatalException(
Messages.ERR_INVALID_METHOD_SIGNATURE.fetch(
el.method.getSignatureString(false), prototype.getSignatureString(true)
),
el.method.getTrace()
);
break;
case MUST_STATIC:
e = new FatalException(
Messages.ERR_CANNOT_MAKE_STATIC_TO_NON_STATIC.fetch(
el.method.getPrototype().getSignatureString(false),
ClassEntity.this.getName()
),
el.method.getTrace()
);
break;
case MUST_NON_STATIC:
e = new FatalException(
Messages.ERR_CANNOT_MAKE_NON_STATIC_TO_STATIC.fetch(
el.method.getPrototype().getSignatureString(false),
ClassEntity.this.getName()
),
el.method.getTrace()
);
break;
case MAGIC_MUST_BE_PUBLIC:
env.error(el.method.getTrace(), el.errorType,
"The magic method %s must have public visibility", el.method.getSignatureString(false)
);
break;
case MUST_BE_PROTECTED:
e = new FatalException(
Messages.ERR_ACCESS_LEVEL_METHOD_MUST_BE_PROTECTED_OR_WEAKER.fetch(
el.method.clazz.getName(),
el.method.getName(),
el.method.getPrototype().getClazz().getName()
),
el.method.getTrace()
);
break;
case MUST_BE_PUBLIC:
e = new FatalException(
Messages.ERR_ACCESS_LEVEL_METHOD_MUST_BE_PUBLIC.fetch(
el.method.clazz.getName(),
el.method.getName(),
el.method.getPrototype().getClazz().getName()
),
el.method.getTrace()
);
break;
case FINAL_ABSTRACT:
e = getException(env, el, Messages.ERR_CANNOT_USE_FINAL_ON_ABSTRACT);
break;
case FINAL:
e = getException(env, el, Messages.ERR_CANNOT_OVERRIDE_FINAL_METHOD, true);
break;
case NON_ABSTRACT:
e = getException(env, el, Messages.ERR_NON_ABSTRACT_METHOD_MUST_CONTAIN_BODY);
break;
case NON_ABSTRACTABLE:
e = getException(env, el, Messages.ERR_ABSTRACT_METHOD_CANNOT_CONTAIN_BODY);
break;
case INVALID_ACCESS_FOR_INTERFACE:
e = getException(env, el, Messages.ERR_ACCESS_TYPE_FOR_INTERFACE_METHOD);
break;
case NON_EXISTS:
if (nonExists == null) nonExists = new HashSet<InvalidMethod>();
nonExists.add(el);
break;
}
if (e != null)
if (env == null)
throw e;
else {
env.error(e.getTraceInfo(), el.errorType, e.getMessage());
}
}
if (nonExists != null) {
StringBuilder needs = new StringBuilder();
Iterator<InvalidMethod> iterator = nonExists.iterator();
int size = 0;
ErrorType errorType = ErrorType.E_NOTICE;
while (iterator.hasNext()) {
InvalidMethod el = iterator.next();
if (el.errorType.value < errorType.value)
errorType = el.errorType;
needs.append(el.method.getClazz().getName())
.append("::")
.append(el.method.getName());
if (iterator.hasNext())
needs.append(", ");
size++;
}
ErrorException e = new FatalException(
Messages.ERR_IMPLEMENT_METHOD.fetch(ClassEntity.this.getName(), size, needs),
ClassEntity.this.getTrace()
);
if (env == null)
throw e;
else {
env.error(e.getTraceInfo(), errorType, e.getMessage());
}
}
}
}
}