718 lines
20 KiB
Java
Executable File
718 lines
20 KiB
Java
Executable File
package danga.s2;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.ListIterator;
|
|
|
|
public class NodeTerm extends NodeExpr
|
|
{
|
|
int type = 0;
|
|
|
|
public final static int INTEGER = 1;
|
|
TokenIntegerLiteral tokInt;
|
|
|
|
public final static int STRING = 2;
|
|
TokenStringLiteral tokStr;
|
|
Node nodeString;
|
|
String ctorclass; // if not null, then we're not a string, but calling a class ctor with a string
|
|
|
|
public final static int BOOL = 3;
|
|
boolean boolValue;
|
|
|
|
public final static int VARREF = 4;
|
|
NodeVarRef var;
|
|
|
|
public final static int SUBEXPR = 5;
|
|
public final static int DEFINEDTEST = 6;
|
|
public final static int SIZEFUNC = 7;
|
|
public final static int REVERSEFUNC = 8;
|
|
public final static int ISNULLFUNC = 12;
|
|
NodeExpr subExpr;
|
|
Type subType; // for backend, set by getType()
|
|
|
|
public final static int NEW = 9;
|
|
public final static int NEWNULL = 13; // Like NEW, but sets object to be null
|
|
TokenIdent newClass;
|
|
|
|
public final static int FUNCCALL = 10;
|
|
public final static int METHCALL = 11;
|
|
int derefLine; // keep track of where we saw the deref token
|
|
TokenIdent funcIdent;
|
|
String funcClass; // null or classname of the method call
|
|
NodeArguments funcArgs;
|
|
boolean funcBuiltin; // is this function call a builtin?
|
|
// (if so, don't use vtable)
|
|
boolean parentMethod; // is this a method call on a super class? if so,
|
|
// can't optimize method call since instance class won't
|
|
// necessarily be known until run-time (without a lot of analysis)
|
|
|
|
String funcID; // used by backend; set after getType()
|
|
String funcID_noclass;
|
|
int funcNum; // used by perl backend for function vtables
|
|
|
|
public final static int ARRAY = 14;
|
|
|
|
public static boolean canStart (Tokenizer toker) throws Exception
|
|
{
|
|
Token t = toker.peek();
|
|
if (t instanceof TokenIntegerLiteral ||
|
|
t instanceof TokenStringLiteral ||
|
|
t instanceof TokenIdent ||
|
|
t.equals(TokenPunct.LPAREN) ||
|
|
t.equals(TokenPunct.DOLLAR) ||
|
|
t.equals(TokenKeyword.DEFINED) ||
|
|
t.equals(TokenKeyword.TRUE) ||
|
|
t.equals(TokenKeyword.FALSE) ||
|
|
t.equals(TokenKeyword.NEW) ||
|
|
t.equals(TokenKeyword.SIZE) ||
|
|
t.equals(TokenKeyword.REVERSE) ||
|
|
t.equals(TokenKeyword.ISNULL) ||
|
|
t.equals(TokenKeyword.NULL) ||
|
|
t.equals(TokenPunct.LBRACK) ||
|
|
t.equals(TokenPunct.LBRACE)
|
|
)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public Type getType (Checker ck) throws Exception
|
|
{
|
|
return getType(ck, null);
|
|
}
|
|
|
|
public Type getType (Checker ck, Type wanted) throws Exception
|
|
{
|
|
if (type == INTEGER) return Type.INT;
|
|
if (type == STRING) {
|
|
if (nodeString != null) {
|
|
return nodeString.getType(ck, Type.STRING);
|
|
}
|
|
if (ck.isStringCtor(wanted)) {
|
|
ctorclass = wanted.baseType();
|
|
return wanted;
|
|
}
|
|
return Type.STRING;
|
|
}
|
|
if (type == SUBEXPR) return subExpr.getType(ck);
|
|
if (type == BOOL) return Type.BOOL;
|
|
|
|
if (type == DEFINEDTEST) {
|
|
System.err.println("FIXME: check type of defined expression");
|
|
return Type.BOOL;
|
|
}
|
|
if (type == SIZEFUNC) {
|
|
subType = subExpr.getType(ck);
|
|
if (subType.equals(Type.STRING))
|
|
return Type.INT;
|
|
if (subType.isArrayOf())
|
|
return Type.INT;
|
|
// complain
|
|
throw new Exception("Can't use size on expression that's "+
|
|
"not a string or array at "+getFilePos());
|
|
|
|
}
|
|
if (type == REVERSEFUNC) {
|
|
subType = subExpr.getType(ck);
|
|
|
|
// reverse a string
|
|
if (subType.equals(Type.STRING))
|
|
return Type.STRING;
|
|
|
|
// reverse an array
|
|
if (subType.isArrayOf())
|
|
return subType;
|
|
|
|
// complain
|
|
throw new Exception("Can't use reverse on expression that's "+
|
|
"not a string or array at "+getFilePos());
|
|
}
|
|
|
|
if (type == ISNULLFUNC) {
|
|
subType = subExpr.getType(ck);
|
|
if (subExpr instanceof NodeTerm) {
|
|
NodeTerm nt = (NodeTerm) subExpr;
|
|
if (nt.type != VARREF && nt.type != FUNCCALL && nt.type != METHCALL)
|
|
throw new Exception("isnull must only be used on an object variable, "+
|
|
"function call or method call at "+getFilePos());
|
|
} else {
|
|
throw new Exception("isnull must only be used on an object variable, "+
|
|
"function call or method call at "+getFilePos());
|
|
}
|
|
|
|
// can't be used on arrays and hashes
|
|
if (subType.isArrayOf() || subType.isHashOf())
|
|
throw new Exception("Can't use isnull on an array or hash at "+getFilePos());
|
|
|
|
// not primitive types either
|
|
if (subType.equals(Type.BOOL) || subType.equals(Type.STRING) || subType.equals(Type.INT))
|
|
throw new Exception("Can't use isnull on primitive types at "+getFilePos());
|
|
|
|
// nor void
|
|
if (subType.equals(Type.VOID))
|
|
throw new Exception("Can't use isnull on a void value at "+getFilePos());
|
|
|
|
return Type.BOOL;
|
|
}
|
|
|
|
if (type == NEW || type == NEWNULL) {
|
|
String clas = newClass.getIdent();
|
|
NodeClass nc = ck.getClass(clas);
|
|
if (nc == null) {
|
|
throw new Exception("Can't instantiate unknown class at "+
|
|
getFilePos());
|
|
}
|
|
return new Type(clas);
|
|
}
|
|
|
|
if (type == VARREF) {
|
|
if (! ck.getInFunction()) {
|
|
throw new Exception("Can't reference a variable outside of a function at "+getFilePos());
|
|
}
|
|
return var.getType(ck, wanted);
|
|
}
|
|
|
|
if (type == METHCALL || type == FUNCCALL) {
|
|
if (! ck.getInFunction()) {
|
|
throw new Exception("Can't call a function or method outside of a function at "+getFilePos());
|
|
}
|
|
|
|
// find the classname of the variable the method was being called on
|
|
if (type == METHCALL) {
|
|
Type vartype = var.getType(ck);
|
|
if (! vartype.isSimple()) {
|
|
throw new Exception("Cannot call a method on an array or hash of "+
|
|
"objects at "+getFilePos());
|
|
}
|
|
funcClass = vartype.toString();
|
|
|
|
NodeClass methClass = ck.getClass(funcClass);
|
|
if (methClass == null) {
|
|
throw new Exception("Can't call a method on an instance of an "+
|
|
"undefined class at "+getFilePos());
|
|
}
|
|
|
|
if (ck.hasDerClasses(funcClass))
|
|
parentMethod = true;
|
|
}
|
|
|
|
funcID = Checker.functionID(funcClass, funcIdent.getIdent(),
|
|
funcArgs.typeList(ck));
|
|
funcBuiltin = ck.isFuncBuiltin(funcID);
|
|
|
|
// and remember the funcID without a class for use later when
|
|
// we have to generate a funcID at run-time by concatenating
|
|
// this to the end of a $instance->{'_type'}
|
|
funcID_noclass = Checker.functionID(null, funcIdent.getIdent(),
|
|
funcArgs.typeList(ck));
|
|
|
|
Type t = ck.functionType(funcID);
|
|
if (! funcBuiltin)
|
|
funcNum = ck.functionNum(funcID);
|
|
if (t == null) {
|
|
throw new Exception("Unknown function "+funcID+" at "+
|
|
funcIdent.getFilePos());
|
|
}
|
|
return t;
|
|
}
|
|
|
|
if (type == ARRAY) {
|
|
return subExpr.getType(ck, wanted);
|
|
}
|
|
|
|
throw new Exception("ERROR: unknown NodeTerm type at "+getFilePos());
|
|
}
|
|
|
|
public boolean isLValue ()
|
|
{
|
|
if (type == VARREF) return true;
|
|
if (type == SUBEXPR) {
|
|
return subExpr.isLValue();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean makeAsString(Checker ck)
|
|
{
|
|
if (type == VARREF) {
|
|
try {
|
|
Type t = var.getType(ck);
|
|
if (t.isSimple()) {
|
|
String bt = t.baseType();
|
|
|
|
// class has .toString() method
|
|
if (ck.classHasToString(bt)) {
|
|
// let's change this VARREF into a METHCALL!
|
|
// warning: ugly hacks ahead...
|
|
type = METHCALL;
|
|
funcIdent = new TokenIdent("toString");
|
|
funcClass = bt;
|
|
funcArgs = (NodeArguments)
|
|
NodeArguments.makeEmptyArgs();
|
|
funcID_noclass = "toString()";
|
|
funcID = bt + "::" + funcID_noclass;
|
|
funcBuiltin = ck.isFuncBuiltin(funcID);
|
|
funcNum = ck.functionNum(funcID);
|
|
if (ck.hasDerClasses(funcClass))
|
|
parentMethod = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
// class has $.as_string string member
|
|
if (ck.classHasAsString(bt)) {
|
|
var.useAsString();
|
|
return true;
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static Node parse (Tokenizer toker) throws Exception
|
|
{
|
|
NodeTerm nt = new NodeTerm();
|
|
|
|
Token t = toker.peek();
|
|
|
|
// integer literal
|
|
if (t instanceof TokenIntegerLiteral) {
|
|
nt.type = NodeTerm.INTEGER;
|
|
nt.tokInt = (TokenIntegerLiteral) nt.eatToken(toker);
|
|
return nt;
|
|
}
|
|
|
|
// boolean literal
|
|
if (t.equals(TokenKeyword.TRUE) || t.equals(TokenKeyword.FALSE)) {
|
|
nt.type = NodeTerm.BOOL;
|
|
nt.boolValue = t.equals(TokenKeyword.TRUE);
|
|
nt.eatToken(toker);
|
|
return nt;
|
|
}
|
|
|
|
// string literal
|
|
if (t instanceof TokenStringLiteral) {
|
|
TokenStringLiteral ts = (TokenStringLiteral) t;
|
|
int ql = ts.getQuotesLeft();
|
|
int qr = ts.getQuotesRight();
|
|
|
|
if (qr != 0) {
|
|
// whole string literal
|
|
nt.type = NodeTerm.STRING;
|
|
nt.tokStr = (TokenStringLiteral) nt.eatToken(toker);
|
|
nt.setStart(nt.tokStr);
|
|
return nt;
|
|
}
|
|
|
|
// interpolated string literal (turn into a subexpr)
|
|
LinkedList toklist = new LinkedList();
|
|
|
|
nt.type = NodeTerm.STRING;
|
|
nt.tokStr = (TokenStringLiteral) nt.eatToken(toker);
|
|
toklist.add(nt.tokStr.clone()); // cloned before it's changed.
|
|
nt.tokStr.setQuotesRight(ql);
|
|
Node lhs = nt;
|
|
FilePos filepos = (FilePos) nt.tokStr.getFilePos();
|
|
|
|
boolean loop = true;
|
|
while (loop) {
|
|
Node rhs = null;
|
|
Token tok = toker.peek();
|
|
if (tok instanceof TokenStringLiteral) {
|
|
rhs = new NodeTerm();
|
|
NodeTerm rhsnt = (NodeTerm) rhs;
|
|
ts = (TokenStringLiteral) tok;
|
|
rhsnt.type = NodeTerm.STRING;
|
|
rhsnt.tokStr = (TokenStringLiteral) rhsnt.eatToken(toker);
|
|
toklist.add(rhsnt.tokStr.clone()); // cloned before it's changed.
|
|
|
|
if (ts.getQuotesRight() == ql) {
|
|
loop = false;
|
|
}
|
|
ts.setQuotesRight(ql);
|
|
ts.setQuotesLeft(ql);
|
|
}
|
|
else if (tok.equals(TokenPunct.DOLLAR)) {
|
|
rhs = NodeTerm.parse(toker);
|
|
toklist.add(rhs);
|
|
}
|
|
else {
|
|
throw new Exception("Error parsing "+
|
|
"interpolated string.");
|
|
}
|
|
|
|
// don't make a sum out of a blank string on either side
|
|
boolean join = true;
|
|
if (lhs instanceof NodeTerm) {
|
|
NodeTerm lhst = (NodeTerm) lhs;
|
|
if (lhst.type == STRING &&
|
|
lhst.tokStr.getString().length() == 0) {
|
|
lhs = rhs;
|
|
join = false;
|
|
}
|
|
}
|
|
if (rhs instanceof NodeTerm) {
|
|
NodeTerm rhst = (NodeTerm) rhs;
|
|
if (rhst.type == STRING &&
|
|
rhst.tokStr.getString().length() == 0) {
|
|
join = false;
|
|
}
|
|
}
|
|
if (join) {
|
|
lhs = new NodeSum(lhs, TokenPunct.PLUS, rhs);
|
|
}
|
|
}
|
|
|
|
lhs.setTokenList(toklist);
|
|
lhs.setStart(filepos);
|
|
|
|
NodeTerm rnt = new NodeTerm();
|
|
rnt.type = NodeTerm.STRING;
|
|
rnt.nodeString = lhs;
|
|
rnt.addNode(lhs);
|
|
return rnt;
|
|
}
|
|
|
|
// Sub-expression (in parenthesis)
|
|
if (t.equals(TokenPunct.LPAREN)) {
|
|
nt.type = NodeTerm.SUBEXPR;
|
|
nt.setStart(nt.eatToken(toker));
|
|
|
|
nt.subExpr = (NodeExpr) NodeExpr.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
|
|
nt.requireToken(toker, TokenPunct.RPAREN);
|
|
return nt;
|
|
}
|
|
|
|
// defined test
|
|
if (t.equals(TokenKeyword.DEFINED)) {
|
|
nt.type = NodeTerm.DEFINEDTEST;
|
|
nt.eatToken(toker);
|
|
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
return nt;
|
|
}
|
|
|
|
// reverse function
|
|
if (t.equals(TokenKeyword.REVERSE)) {
|
|
nt.type = NodeTerm.REVERSEFUNC;
|
|
nt.eatToken(toker);
|
|
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
return nt;
|
|
}
|
|
|
|
// size function
|
|
if (t.equals(TokenKeyword.SIZE)) {
|
|
nt.type = NodeTerm.SIZEFUNC;
|
|
nt.eatToken(toker);
|
|
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
return nt;
|
|
}
|
|
|
|
// isnull function
|
|
if (t.equals(TokenKeyword.ISNULL)) {
|
|
nt.type = NodeTerm.ISNULLFUNC;
|
|
nt.eatToken(toker);
|
|
nt.subExpr = (NodeTerm) NodeTerm.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
return nt;
|
|
}
|
|
|
|
// new and null
|
|
if (t.equals(TokenKeyword.NEW) || t.equals(TokenKeyword.NULL)) {
|
|
nt.type = (t.equals(TokenKeyword.NEW) ? NodeTerm.NEW : NodeTerm.NEWNULL);
|
|
nt.eatToken(toker);
|
|
nt.newClass = nt.getIdent(toker);
|
|
return nt;
|
|
}
|
|
|
|
// VarRef
|
|
if (t.equals(TokenPunct.DOLLAR)) {
|
|
nt.type = VARREF;
|
|
nt.var = (NodeVarRef) NodeVarRef.parse(toker);
|
|
nt.addNode(nt.var);
|
|
|
|
// check for -> after, like: $object->method(arg1, arg2, ...)
|
|
if (toker.peek().equals(TokenPunct.DEREF)) {
|
|
nt.derefLine = toker.peek().getFilePos().line;
|
|
|
|
nt.eatToken(toker);
|
|
nt.type = METHCALL;
|
|
// don't return... parsing continues below.
|
|
} else {
|
|
return nt;
|
|
}
|
|
}
|
|
|
|
// function/method call
|
|
if (t instanceof TokenIdent || nt.type == METHCALL) {
|
|
if (nt.type != METHCALL) nt.type = FUNCCALL;
|
|
|
|
nt.funcIdent = nt.getIdent(toker);
|
|
nt.funcArgs = (NodeArguments) NodeArguments.parse(toker);
|
|
nt.addNode(nt.funcArgs);
|
|
return nt;
|
|
}
|
|
|
|
// array/hash literal
|
|
if (NodeArrayLiteral.canStart(toker)) {
|
|
nt.type = ARRAY;
|
|
nt.subExpr = (NodeExpr) NodeArrayLiteral.parse(toker);
|
|
nt.addNode(nt.subExpr);
|
|
return nt;
|
|
}
|
|
|
|
throw new Exception("Can't finish parsing NodeTerm at " +
|
|
toker.locationString() +
|
|
", toker.peek() = " + t.toString());
|
|
}
|
|
|
|
public void asS2 (Indenter o)
|
|
{
|
|
if (type == INTEGER) {
|
|
tokInt.asS2(o);
|
|
return;
|
|
}
|
|
if (type == STRING) {
|
|
if (nodeString != null) {
|
|
nodeString.asS2(o);
|
|
return;
|
|
}
|
|
tokStr.asS2(o);
|
|
return;
|
|
}
|
|
if (type == BOOL) {
|
|
if (boolValue)
|
|
o.write("true");
|
|
else
|
|
o.write("false");
|
|
return;
|
|
}
|
|
if (type == SUBEXPR) {
|
|
o.write("(");
|
|
subExpr.asS2(o);
|
|
o.write(")");
|
|
return;
|
|
}
|
|
if (type == NEW) {
|
|
o.write("new ");
|
|
o.write(newClass.getIdent());
|
|
return;
|
|
}
|
|
if (type == NEWNULL) {
|
|
o.write("null ");
|
|
o.write(newClass.getIdent());
|
|
return;
|
|
}
|
|
if (type == DEFINEDTEST) {
|
|
o.write("defined ");
|
|
subExpr.asS2(o);
|
|
return;
|
|
}
|
|
if (type == SIZEFUNC) {
|
|
o.write("size ");
|
|
subExpr.asS2(o);
|
|
return;
|
|
}
|
|
if (type == REVERSEFUNC) {
|
|
o.write("reverse ");
|
|
subExpr.asS2(o);
|
|
return;
|
|
}
|
|
if (type == ISNULLFUNC) {
|
|
o.write("isnull ");
|
|
subExpr.asS2(o);
|
|
return;
|
|
}
|
|
if (type == VARREF || type == METHCALL) {
|
|
var.asS2(o);
|
|
}
|
|
if (type == METHCALL) {
|
|
o.write("->");
|
|
}
|
|
if (type == METHCALL || type == FUNCCALL) {
|
|
o.write(funcIdent.getIdent());
|
|
funcArgs.asS2(o);
|
|
}
|
|
if (type == VARREF || type == METHCALL || type == FUNCCALL)
|
|
return;
|
|
if (type == ARRAY) {
|
|
subExpr.asS2(o);
|
|
return;
|
|
}
|
|
}
|
|
|
|
public void asPerl (BackendPerl bp, Indenter o)
|
|
{
|
|
if (type == INTEGER) {
|
|
tokInt.asPerl(bp, o);
|
|
return;
|
|
}
|
|
if (type == STRING) {
|
|
if (nodeString != null) {
|
|
o.write("(");
|
|
nodeString.asPerl(bp, o);
|
|
o.write(")");
|
|
return;
|
|
}
|
|
if (ctorclass != null)
|
|
o.write("S2::Builtin::"+ctorclass+"__"+ctorclass+"(");
|
|
tokStr.asPerl(bp, o);
|
|
if (ctorclass != null)
|
|
o.write(")");
|
|
return;
|
|
}
|
|
if (type == BOOL) {
|
|
if (boolValue)
|
|
o.write("1");
|
|
else
|
|
o.write("0");
|
|
return;
|
|
}
|
|
if (type == SUBEXPR) {
|
|
o.write("(");
|
|
subExpr.asPerl(bp, o);
|
|
o.write(")");
|
|
return;
|
|
}
|
|
if (type == ARRAY) {
|
|
subExpr.asPerl(bp, o);
|
|
return;
|
|
}
|
|
if (type == NEW) {
|
|
o.write("{'_type'=>" +
|
|
bp.quoteString(newClass.getIdent())+
|
|
"}");
|
|
return;
|
|
}
|
|
if (type == NEWNULL) {
|
|
o.write("{'_type'=>" +
|
|
bp.quoteString(newClass.getIdent())+
|
|
", '_isnull'=>1}");
|
|
return;
|
|
}
|
|
if (type == DEFINEDTEST) {
|
|
o.write("defined(");
|
|
subExpr.asPerl(bp, o);
|
|
o.write(")");
|
|
return;
|
|
}
|
|
if (type == REVERSEFUNC) {
|
|
if (subType.isArrayOf()) {
|
|
o.write("[reverse(");
|
|
o.write("@{");
|
|
subExpr.asPerl(bp, o);
|
|
o.write("})");
|
|
o.write("]");
|
|
} else if (subType.equals(Type.STRING)) {
|
|
o.write("reverse(");
|
|
subExpr.asPerl(bp, o);
|
|
o.write(")");
|
|
}
|
|
return;
|
|
}
|
|
if (type == SIZEFUNC) {
|
|
if (subType.equals(Type.STRING)) {
|
|
o.write("length(");
|
|
subExpr.asPerl(bp, o);
|
|
o.write(")");
|
|
}
|
|
else if (subType.isArrayOf()) {
|
|
o.write("scalar(@{");
|
|
subExpr.asPerl(bp, o);
|
|
o.write("})");
|
|
}
|
|
return;
|
|
}
|
|
if (type == ISNULLFUNC) {
|
|
o.write("(ref ");
|
|
subExpr.asPerl(bp, o);
|
|
o.write(" ne \"HASH\" || ");
|
|
subExpr.asPerl(bp, o);
|
|
o.write("->{'_isnull'})");
|
|
return;
|
|
}
|
|
if (type == VARREF) {
|
|
var.asPerl(bp, o);
|
|
return;
|
|
}
|
|
|
|
if (type == FUNCCALL || type == METHCALL) {
|
|
|
|
boolean funcDumped = false;
|
|
|
|
// builtin functions can be optimized.
|
|
if (funcBuiltin) {
|
|
// these built-in functions can be inlined.
|
|
if (funcID.equals("string(int)")) {
|
|
funcArgs.asPerl(bp, o, false);
|
|
return;
|
|
}
|
|
if (funcID.equals("int(string)")) {
|
|
// cast from string to int by adding zero to it
|
|
o.write("(0+");
|
|
funcArgs.asPerl(bp, o, false);
|
|
o.write(")");
|
|
return;
|
|
}
|
|
|
|
// otherwise, call the builtin function (avoid a layer
|
|
// of indirection), unless it's for a class that has
|
|
// children (won't know until run-time which class to call)
|
|
if(funcClass == null || (funcClass != null && ! parentMethod)) {
|
|
o.write("S2::Builtin::");
|
|
if (funcClass != null) {
|
|
o.write(funcClass + "__");
|
|
}
|
|
o.write(funcIdent.getIdent());
|
|
funcDumped = true;
|
|
}
|
|
}
|
|
|
|
if (funcDumped == false) {
|
|
if (type == METHCALL && ! funcClass.equals("string")) {
|
|
o.write("$_ctx->[VTABLE]->{get_object_func_num(");
|
|
o.write(bp.quoteString(funcClass));
|
|
o.write(",");
|
|
var.asPerl(bp, o);
|
|
o.write(",");
|
|
o.write(bp.quoteString(funcID_noclass));
|
|
o.write(",");
|
|
o.write(bp.getLayerID());
|
|
o.write(",");
|
|
o.write(derefLine);
|
|
if (var.isSuper()) {
|
|
o.write(",1");
|
|
}
|
|
o.write(")}->");
|
|
} else if (type == METHCALL) {
|
|
o.write("$_ctx->[VTABLE]->{get_func_num(");
|
|
o.write(bp.quoteString(funcID));
|
|
o.write(")}->");
|
|
} else {
|
|
o.write("$_ctx->[VTABLE]->{$_l2g_func["+funcNum+"]}->");
|
|
}
|
|
}
|
|
|
|
o.write("($_ctx, ");
|
|
|
|
// this pointer
|
|
if (type == METHCALL) {
|
|
var.asPerl(bp, o);
|
|
o.write(", ");
|
|
}
|
|
|
|
funcArgs.asPerl(bp, o, false);
|
|
|
|
o.write(")");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
}
|