/**
* 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 static me.qmx.jitescript.util.CodegenUtils.*;
import java.lang.invoke.CallSite;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import me.qmx.jitescript.CodeBlock;
import org.dynjs.exception.ThrowException;
import org.dynjs.parser.Statement;
import org.dynjs.runtime.*;
import org.dynjs.runtime.Completion.Type;
import me.qmx.jitescript.internal.org.objectweb.asm.tree.LabelNode;
public abstract class AbstractStatement implements Statement {
private final static AtomicInteger counter = new AtomicInteger();
private int number;
private List<String> labels = new ArrayList<String>();
AbstractStatement() {
this.number = counter.incrementAndGet();
}
public int getStatementNumber() {
return this.number;
}
public void addLabel(String label) {
this.labels.add(label);
}
public List<String> getLabels() {
return this.labels;
}
public List<FunctionDeclaration> getFunctionDeclarations() {
return Collections.emptyList();
}
public List<VariableDeclaration> getVariableDeclarations() {
return Collections.emptyList();
}
public CodeBlock normalCompletion() {
return new CodeBlock()
.invokestatic(p(Completion.class), "createNormal", sig(Completion.class));
}
public CodeBlock normalCompletionWithValue() {
return new CodeBlock()
// IN: val
.invokestatic(p(Completion.class), "createNormal", sig(Completion.class, Object.class));
}
public CodeBlock returnCompletion() {
return new CodeBlock()
// IN value
.invokestatic(p(Completion.class), "createReturn", sig(Completion.class, Object.class));
// completion
}
public CodeBlock continueCompletion(final String target) {
CodeBlock codeBlock = new CodeBlock();
// <EMPTY>
if (target == null) {
codeBlock.aconst_null();
} else {
codeBlock.ldc(target);
}
// target
codeBlock.invokestatic(p(Completion.class), "createContinue", sig(Completion.class, String.class));
// completion
return codeBlock;
}
public CodeBlock breakCompletion(final String target) {
CodeBlock codeBlock = new CodeBlock();
// <EMPTY>
if (target == null) {
codeBlock.aconst_null();
} else {
codeBlock.ldc(target);
}
// target
codeBlock.invokestatic(p(Completion.class), "createBreak", sig(Completion.class, String.class));
// completion
return codeBlock;
}
public CodeBlock throwCompletion() {
return new CodeBlock()
// IN value
.invokestatic(p(Completion.class), "createThrow", sig(Completion.class, Object.class));
// completion
}
public CodeBlock handleCompletion(
final LabelNode normalTarget,
final LabelNode breakTarget,
final LabelNode continueTarget,
final LabelNode returnTarget) {
return new CodeBlock()
// IN: completion
.append(jsCompletionType())
.lookupswitch(normalTarget,
new int[] { Type.NORMAL.ordinal(), Type.BREAK.ordinal(), Type.CONTINUE.ordinal(), Type.RETURN.ordinal() },
new LabelNode[] { normalTarget, breakTarget, continueTarget, returnTarget });
}
public CodeBlock convertToNormal() {
return new CodeBlock()
// IN: completion
.dup()
// completion completion
.getstatic(p(Completion.Type.class), "NORMAL", ci(Type.class))
// completion completion NORMAL
.putfield(p(Completion.class), "type", ci(Type.class));
// completion
}
public CodeBlock jsCompletionValue() {
return new CodeBlock()
// IN completion
.getfield(p(Completion.class), "value", ci(Object.class));
// value
}
public CodeBlock jsCompletionTarget() {
return new CodeBlock()
// IN completion
.getfield(p(Completion.class), "target", ci(String.class));
// value
}
public CodeBlock jsCompletionType() {
return new CodeBlock()
// IN completion
.getfield(p(Completion.class), "type", ci(Completion.Type.class))
// type
.invokevirtual(p(Completion.Type.class), "ordinal", sig(int.class));
}
public String dump(String indent) {
String data = dumpData();
final String line = getPosition() == null ? "?" : getPosition().getLine() + "";
return indent + getClass().getSimpleName() + ":" + line + " " +
(data != null ? (" (" + data + ")") : "") + "\n";
}
public String dumpData() {
return null;
}
protected Object getValue(CallSite callSite, ExecutionContext context, Object obj) {
if (obj instanceof Reference) {
Reference ref = (Reference) obj;
String name = ref.getReferencedName();
try {
Object result = callSite.getTarget().invoke( obj, context, name );
return result;
} catch (ThrowException e) {
throw e;
} catch (NoSuchMethodError e) {
if (ref.isPropertyReference() && !ref.isUnresolvableReference()) {
return Types.UNDEFINED;
}
throw new ThrowException(context, context.createReferenceError("unable to reference: " + name));
} catch (Throwable e) {
throw new ThrowException(context, e);
}
} else {
return obj;
}
}
protected Completion invokeCompiledBlockStatement(ExecutionContext context, String grist, Statement statement) {
BasicBlock block = compiledBlockStatement(context, grist, statement);
return block.call(context);
}
protected BasicBlock compiledBlockStatement(ExecutionContext context, String grist, Statement statement) {
BlockManager.Entry entry = context.getBlockManager().retrieve(statement.getStatementNumber());
if (entry.getCompiled() == null) {
BasicBlock compiledBlock = context.getCompiler().compileBasicBlock(context, grist, statement, context.isStrict());
entry.setCompiled(compiledBlock);
}
return (BasicBlock) entry.getCompiled();
}
}