/**
* Copyright 2013 Douglas Campos, and individual contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dynjs.parser.ast;
import org.dynjs.codegen.DereferencedReference;
import org.dynjs.exception.ThrowException;
import org.dynjs.parser.CodeVisitor;
import org.dynjs.parser.js.Position;
import org.dynjs.runtime.EnvironmentRecord;
import org.dynjs.runtime.ExecutionContext;
import org.dynjs.runtime.Reference;
import org.dynjs.runtime.Types;
import org.dynjs.runtime.linker.DynJSBootstrapper;
import java.lang.invoke.CallSite;
import java.util.ArrayList;
import java.util.List;
public class FunctionCallExpression extends AbstractExpression {
private final Expression memberExpr;
private final List<Expression> argExprs;
private final CallSite functionGet;
private final List<CallSite> argGets;
private final CallSite functionCall;
public FunctionCallExpression(Expression memberExpr, List<Expression> argExprs) {
this.memberExpr = memberExpr;
this.argExprs = argExprs;
functionGet = DynJSBootstrapper.factory().createGet( memberExpr.getPosition() );
this.argGets = new ArrayList<>();
for ( Expression each : argExprs ) {
this.argGets.add( DynJSBootstrapper.factory().createGet( each.getPosition() ) );
}
this.functionCall = DynJSBootstrapper.factory().createCall( memberExpr.getPosition() );
}
public Position getPosition() {
return this.memberExpr.getPosition();
}
public List<Expression> getArgumentExpressions() {
return this.argExprs;
}
public Expression getMemberExpression() {
return this.memberExpr;
}
public int getSizeMetric() {
int size = this.memberExpr.getSizeMetric();
for ( Expression each : argExprs ) {
size += each.getSizeMetric();
}
return size + 5;
}
@Override
public Object interpret(ExecutionContext context, boolean debug) {
Object ref = getMemberExpression().interpret(context, debug);
Object function = getValue(this.functionGet, context, ref);
List<Expression> argExprs = getArgumentExpressions();
Object[] args = new Object[argExprs.size()];
int numArgs = argExprs.size();
for ( int i = 0 ; i < numArgs ; ++i ) {
Expression each = this.argExprs.get(i);
CallSite eachGet = this.argGets.get(i);
Object value = getValue(eachGet, context, each.interpret(context, debug));
//System.err.println( "ARG: " + i + " -> " + each + " // " + value );
args[i] = value;
}
Object thisValue = null;
if (ref instanceof Reference) {
if (((Reference) ref).isPropertyReference()) {
thisValue = ((Reference) ref).getBase();
} else {
thisValue = ((EnvironmentRecord) ((Reference) ref).getBase()).implicitThisValue();
}
}
if (thisValue == null) {
thisValue = Types.UNDEFINED;
}
if (ref instanceof Reference) {
function = new DereferencedReference((Reference) ref, function);
}
try {
//System.err.println( "CALL: "+ function + " // " + thisValue + " :: " + Arrays.asList( args ) );
return this.functionCall.getTarget().invoke( function, context, thisValue, args);
} catch (ThrowException e) {
throw e;
} catch (NoSuchMethodError e) {
throw new ThrowException((ExecutionContext) context, ((ExecutionContext) context).createTypeError("not callable: " + function.toString()));
} catch (Throwable e) {
throw new ThrowException((ExecutionContext) context, e);
}
}
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(this.memberExpr).append("(");
boolean first = true;
for (Expression each : this.argExprs) {
if (!first) {
buf.append(", ");
}
buf.append(each.toString());
first = false;
}
buf.append(")");
return buf.toString();
}
public String dump(String indent) {
StringBuilder buffer = new StringBuilder();
buffer.append( super.dump( indent ) );
buffer.append( this.memberExpr.dump( indent + " " ) );
for ( Expression arg : argExprs ) {
buffer.append( arg.dump( indent + " " ) );
}
return buffer.toString();
}
@Override
public Object accept(Object context, CodeVisitor visitor, boolean strict) {
return visitor.visit( context, this, strict );
}
}