package php.runtime.ext.core.classes;
import php.runtime.Memory;
import php.runtime.common.HintType;
import php.runtime.common.Messages;
import php.runtime.env.*;
import php.runtime.env.Package;
import php.runtime.invoke.Invoker;
import php.runtime.lang.BaseObject;
import php.runtime.memory.ArrayMemory;
import php.runtime.memory.ObjectMemory;
import php.runtime.memory.ReferenceMemory;
import php.runtime.memory.TrueMemory;
import php.runtime.reflection.ClassEntity;
import php.runtime.reflection.FunctionEntity;
import static php.runtime.annotation.Reflection.*;
@Name("php\\lang\\Environment")
public class WrapEnvironment extends BaseObject {
public final static int CONCURRENT = 1;
public final static int HOT_RELOAD = 2;
protected Environment environment;
protected Invoker onMessage;
public WrapEnvironment(Environment env, Environment wrapEnv) {
super(env);
setEnvironment(wrapEnv);
}
public WrapEnvironment(Environment env, ClassEntity clazz) {
super(env, clazz);
}
public Environment getWrapEnvironment() {
return environment;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Signature({
@Arg(value = "parent", nativeType = WrapEnvironment.class, optional = @Optional("NULL")),
@Arg(value = "flags", optional = @Optional(value = "0", type = HintType.INT))
})
public Memory __construct(Environment env, Memory... args){
CompileScope scope = env.scope;
int flags = args[1].toInteger();
boolean hotReload = (flags & HOT_RELOAD) == HOT_RELOAD;
boolean concurrent = (flags & CONCURRENT) == CONCURRENT;
if (hotReload) {
scope = new CompileScope(scope);
}
if (args[0].isNull()) {
if (concurrent)
setEnvironment(new ConcurrentEnvironment(scope, env.getDefaultBuffer().getOutput()));
else
setEnvironment(new Environment(scope, env.getDefaultBuffer().getOutput()));
} else {
if (hotReload)
env.exception("Environment cannot be hot-reloadable with parent");
if (concurrent)
setEnvironment(new ConcurrentEnvironment(args[0].toObject(WrapEnvironment.class).getWrapEnvironment()));
else
setEnvironment(new Environment(args[0].toObject(WrapEnvironment.class).getWrapEnvironment()));
}
return Memory.NULL;
}
@Signature(@Arg(value = "runnable"))
public Memory execute(Environment env, Memory... args) throws Throwable {
Invoker invoker = Invoker.valueOf(this.environment, null, args[0]);
if (invoker == null) {
env.exception("Argument 1 must be callable in environment");
return Memory.NULL;
}
invoker.setTrace(env.trace());
return invoker.call();
}
@Signature(@Arg(value = "sourceMap", nativeType = WrapSourceMap.class))
public Memory registerSourceMap(Environment env, Memory... args) {
this.environment.registerSourceMap(args[0].toObject(WrapSourceMap.class).getWrappedObject());
return Memory.NULL;
}
@Signature(@Arg(value = "sourceMap", nativeType = WrapSourceMap.class))
public Memory unregisterSourceMap(Environment env, Memory... args) {
this.environment.unregisterSourceMap(args[0].toObject(WrapSourceMap.class).getWrappedObject());
return Memory.NULL;
}
@Signature(@Arg("className"))
public Memory exportClass(final Environment env, Memory... args) throws Throwable {
ClassEntity classEntity = environment.fetchClass(args[0].toString());
if (classEntity == null) {
env.exception(Messages.ERR_CLASS_NOT_FOUND.fetch(args[0]));
return Memory.NULL;
}
env.registerClass(classEntity);
return Memory.NULL;
}
@Signature
public Memory importAutoLoaders(Environment env, Memory... args) {
for (SplClassLoader loader : env.getClassLoaders()) {
environment.registerAutoloader(loader.forEnvironment(environment), false);
}
return Memory.NULL;
}
@Signature(@Arg("className"))
public Memory importClass(Environment env, Memory... args) throws Throwable {
ClassEntity classEntity = env.fetchClass(args[0].toString());
if (classEntity == null) {
env.exception(Messages.ERR_CLASS_NOT_FOUND.fetch(args[0]));
return Memory.NULL;
}
environment.registerClass(classEntity);
return Memory.NULL;
}
@Signature(@Arg("functionName"))
public Memory importFunction(Environment env, Memory... args){
FunctionEntity functionEntity = env.fetchFunction(args[0].toString());
if (functionEntity == null) {
env.exception(Messages.ERR_FUNCTION_NOT_FOUND.fetch(args[0]));
return Memory.NULL;
}
environment.registerFunction(functionEntity);
return Memory.NULL;
}
@Signature(@Arg("functionName"))
public Memory exportFunction(Environment env, Memory... args){
FunctionEntity functionEntity = environment.fetchFunction(args[0].toString());
if (functionEntity == null) {
env.exception(Messages.ERR_FUNCTION_NOT_FOUND.fetch(args[0]));
return Memory.NULL;
}
env.registerFunction(functionEntity);
return Memory.NULL;
}
@Signature({
@Arg("name"), @Arg("value"),
@Arg(value = "caseSensitive", optional = @Optional(value = "true", type = HintType.BOOLEAN))
})
public Memory defineConstant(Environment env, Memory... args){
Memory val = args[1].toValue();
if (val.isArray() || val.isObject())
env.exception("Argument 2 must be a scalar value");
if (!environment.defineConstant(args[0].toString(), val, args[2].toBoolean()))
env.exception("Constant '%s' already registered", args[0]);
return Memory.NULL;
}
@Signature(@Arg("callback"))
public Memory onOutput(Environment env, Memory... args) {
if (args[0].isNull()) {
Invoker invoker = Invoker.valueOf(this.environment, null, args[0]);
if (invoker == null) {
env.exception("Argument 1 must be callable in environment");
return Memory.NULL;
}
invoker.setTrace(env.trace());
this.environment.getDefaultBuffer().setCallback(args[0], invoker);
} else {
this.environment.getDefaultBuffer().setCallback(null);
}
return Memory.NULL;
}
@Signature(@Arg("callback"))
public Memory onMessage(Environment env, Memory... args) {
Invoker invoker = Invoker.valueOf(this.environment, null, args[0]);
if (invoker == null) {
env.exception("Argument 1 must be callable in environment");
return Memory.NULL;
}
onMessage = invoker;
return Memory.NULL;
}
@Signature(@Arg("message"))
public Memory sendMessage(Environment env, Memory... args) throws Throwable {
if (onMessage == null) {
env.exception("Environment cannot receive messages, onMessage callback is NULL");
return Memory.NULL;
}
return onMessage.call(args);
}
@Signature(@Arg("path"))
public Memory findModule(Environment env, Memory... args) throws Throwable {
ModuleManager moduleManager = this.environment.getModuleManager();
boolean hasModule = moduleManager.hasModule(args[0].toString());
if (hasModule) {
return ObjectMemory.valueOf(new WrapModule(env, moduleManager.fetchModule(args[0].toString())));
}
return Memory.NULL;
}
@Signature({
@Arg("name"), @Arg(value = "package", nativeType = WrapPackage.class)
})
public Memory setPackage(Environment env, Memory... args) {
PackageManager packageManager = this.environment.getPackageManager();
packageManager.set(args[0].toString(), args[1].toObject(WrapPackage.class).getPackage());
return Memory.NULL;
}
@Signature
public Memory getPackages(Environment env, Memory... args) {
PackageManager packageManager = this.environment.getPackageManager();
ArrayMemory result = new ArrayMemory();
for (String name : packageManager.names()) {
result.add(new WrapPackage(env, packageManager.fetch(name)));
}
return result.toConstant();
}
@Signature(@Arg("name"))
public Memory hasPackage(Environment env, Memory... args) {
return TrueMemory.valueOf(this.environment.getPackageManager().has(args[0].toString()));
}
@Signature(@Arg("name"))
public Memory getPackage(Environment env, Memory... args) {
PackageManager packageManager = this.environment.getPackageManager();
if (packageManager.has(args[0].toString())) {
Package aPackage = packageManager.tryFind(args[0].toString(), env.trace());
if (aPackage == null) {
return Memory.NULL;
}
return ObjectMemory.valueOf(new WrapPackage(env, aPackage));
} else {
return Memory.NULL;
}
}
@Signature
public Memory __destruct(Environment env, Memory... args) throws Throwable {
environment.doFinal();
return Memory.NULL;
}
@Signature
public static Memory current(Environment env, Memory... args){
return new ObjectMemory(new WrapEnvironment(env, env));
}
}