2019-12-28 21:29:41 +00:00
|
|
|
/**
|
|
|
|
* Serene (simple) - A PoC lisp to collect data on Serenes concepts
|
|
|
|
* Copyright (C) 2019-2020 Sameer Rahmani <lxsameer@gnu.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301, USA.
|
|
|
|
*/
|
2019-12-28 19:46:36 +00:00
|
|
|
package serene.simple.builtin;
|
|
|
|
|
|
|
|
import java.lang.reflect.Constructor;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import serene.simple.BaseScope;
|
|
|
|
import serene.simple.SereneException;
|
|
|
|
import static serene.simple.Utils.getClassOf;
|
|
|
|
|
|
|
|
public class NewFn extends AFn {
|
|
|
|
public String fnName() {
|
|
|
|
return "new";
|
|
|
|
};
|
|
|
|
|
|
|
|
public Object eval(BaseScope scope) throws SereneException {
|
|
|
|
List<Object> args = this.arguments();
|
|
|
|
|
|
|
|
if (args.size() == 0) {
|
|
|
|
throw new SereneException("Needs a class to instantiate.");
|
|
|
|
}
|
|
|
|
else if(args.size() == 1) {
|
|
|
|
return this.createObject((Class<?>) args.get(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.createObject((Class<?>) args.get(0), args.subList(1, args.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
private Object createObject(Class<?> c) {
|
|
|
|
return this.createObject(c, (List<Object>) new ArrayList<Object>());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Object createObject(Class c, List<Object> args) {
|
|
|
|
Constructor<?> constructor = this.getConstructor(c, args);
|
|
|
|
//Object[] argvals = new Object[args.size()];
|
|
|
|
Object[] argvals = (Object[]) args.toArray();
|
|
|
|
|
|
|
|
for(int i = 0; i < args.size(); i++)
|
|
|
|
|
|
|
|
if(constructor != null) {
|
|
|
|
try {
|
|
|
|
return constructor.newInstance(
|
|
|
|
Reflector.boxArgs(constructor.getParameterTypes(), argvals));
|
|
|
|
}
|
|
|
|
catch(Exception e) {
|
|
|
|
throw new SereneException(
|
|
|
|
String.format("Can't instantiate class '%s' because: %s", c.getName(), e.getMessage()));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Reflector.invokeConstructor(c, argvals);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Constructor<?> getConstructor(Class<?> c, List<Object> args) {
|
|
|
|
ArrayList<Constructor<?>> possibleInitFns = new ArrayList<Constructor<?>>();
|
|
|
|
Constructor<?>[] inits = c.getConstructors();
|
|
|
|
ArrayList<Class<?>[]> params = new ArrayList<Class<?>[]>();
|
|
|
|
ArrayList<Class<?>> rets = new ArrayList<Class<?>>();
|
|
|
|
// Constructor index on the possibleInitFns List
|
|
|
|
int id = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < inits.length; i++){
|
|
|
|
Constructor<?> initFn = inits[i];
|
|
|
|
if(initFn.getParameterTypes().length == args.size())
|
|
|
|
{
|
|
|
|
possibleInitFns.add(initFn);
|
|
|
|
params.add(initFn.getParameterTypes());
|
|
|
|
rets.add(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (possibleInitFns.isEmpty()) {
|
|
|
|
throw new IllegalArgumentException("No matching constructor found for " + c);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (possibleInitFns.size() > 1) {
|
|
|
|
id = findMatchingConstructor(c.getName(), params, args, rets);
|
|
|
|
}
|
|
|
|
|
|
|
|
return id == 0 ? null : (Constructor<?>) possibleInitFns.get(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public int findMatchingConstructor(String methodName,
|
|
|
|
ArrayList<Class<?>[]> paramlists,
|
|
|
|
List<Object> args,
|
|
|
|
List<Class<?>> rets) {
|
|
|
|
// presumes matching lengths
|
|
|
|
int matchIdx = -1;
|
|
|
|
boolean tied = false;
|
|
|
|
boolean foundExact = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < paramlists.size(); i++) {
|
|
|
|
boolean match = true;
|
|
|
|
int exact = 0;
|
|
|
|
|
|
|
|
for (int p = 0; match && p < args.size(); ++p) {
|
|
|
|
Object arg = args.get(p);
|
|
|
|
Class<?> aclass = getClassOf(arg);
|
|
|
|
Class<?> pclass = paramlists.get(i)[p];
|
|
|
|
|
|
|
|
// TODO: handle null aclass here here
|
|
|
|
if (aclass == pclass)
|
|
|
|
exact++;
|
|
|
|
else
|
|
|
|
match = Reflector.paramArgTypeMatch(pclass, aclass);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exact == args.size()) {
|
|
|
|
if (!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i)))
|
|
|
|
matchIdx = i;
|
|
|
|
|
|
|
|
tied = false;
|
|
|
|
foundExact = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (match && !foundExact) {
|
|
|
|
if (matchIdx == -1)
|
|
|
|
matchIdx = i;
|
|
|
|
else {
|
|
|
|
if (subsumes(paramlists.get(i), paramlists.get(matchIdx))) {
|
|
|
|
matchIdx = i;
|
|
|
|
tied = false;
|
|
|
|
}
|
|
|
|
else if (Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) {
|
|
|
|
if (rets.get(matchIdx).isAssignableFrom(rets.get(i)))
|
|
|
|
matchIdx = i;
|
|
|
|
}
|
|
|
|
else if (!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))
|
|
|
|
tied = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tied)
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"More than one matching method found: " + methodName);
|
|
|
|
|
|
|
|
return matchIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static public boolean subsumes(Class<?>[] c1, Class<?>[] c2){
|
|
|
|
//Presumes matching lengths
|
|
|
|
Boolean better = false;
|
|
|
|
|
|
|
|
for(int i = 0; i < c1.length; i++) {
|
|
|
|
if(c1[i] != c2[i]) {
|
|
|
|
if(!c1[i].isPrimitive() && c2[i].isPrimitive()
|
|
|
|
||
|
|
|
|
c2[i].isAssignableFrom(c1[i]))
|
|
|
|
better = true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return better;
|
|
|
|
}
|
|
|
|
}
|