private void GenerateNativeFunctionOverrides(ClassFileWriter cfw, string encodedSource)
{
// Override NativeFunction.getLanguageVersion() with
// public int getLanguageVersion() { return <version-constant>; }
cfw.StartMethod("getLanguageVersion", "()I", ClassFileWriter.ACC_PUBLIC);
cfw.AddPush(compilerEnv.GetLanguageVersion());
cfw.Add(ByteCode.IRETURN);
// 1: this and no argument or locals
cfw.StopMethod((short)1);
// The rest of NativeFunction overrides require specific code for each
// script/function id
int Do_getFunctionName = 0;
int Do_getParamCount = 1;
int Do_getParamAndVarCount = 2;
int Do_getParamOrVarName = 3;
int Do_getEncodedSource = 4;
int Do_getParamOrVarConst = 5;
int SWITCH_COUNT = 6;
for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex)
{
if (methodIndex == Do_getEncodedSource && encodedSource == null)
{
continue;
}
// Generate:
// prologue;
// switch over function id to implement function-specific action
// epilogue
short methodLocals;
switch (methodIndex)
{
case Do_getFunctionName:
{
methodLocals = 1;
// Only this
cfw.StartMethod("getFunctionName", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
break;
}
case Do_getParamCount:
{
methodLocals = 1;
// Only this
cfw.StartMethod("getParamCount", "()I", ClassFileWriter.ACC_PUBLIC);
break;
}
case Do_getParamAndVarCount:
{
methodLocals = 1;
// Only this
cfw.StartMethod("getParamAndVarCount", "()I", ClassFileWriter.ACC_PUBLIC);
break;
}
case Do_getParamOrVarName:
{
methodLocals = 1 + 1;
// this + paramOrVarIndex
cfw.StartMethod("getParamOrVarName", "(I)Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
break;
}
case Do_getParamOrVarConst:
{
methodLocals = 1 + 1 + 1;
// this + paramOrVarName
cfw.StartMethod("getParamOrVarConst", "(I)Z", ClassFileWriter.ACC_PUBLIC);
break;
}
case Do_getEncodedSource:
{
methodLocals = 1;
// Only this
cfw.StartMethod("getEncodedSource", "()Ljava/lang/String;", ClassFileWriter.ACC_PUBLIC);
cfw.AddPush(encodedSource);
break;
}
default:
{
throw Kit.CodeBug();
}
}
int count = scriptOrFnNodes.Length;
int switchStart = 0;
int switchStackTop = 0;
if (count > 1)
{
// Generate switch but only if there is more then one
// script/function
cfw.AddLoadThis();
cfw.Add(ByteCode.GETFIELD, cfw.GetClassName(), ID_FIELD_NAME, "I");
// do switch from 1 .. count - 1 mapping 0 to the default case
switchStart = cfw.AddTableSwitch(1, count - 1);
}
for (int i = 0; i != count; ++i)
{
ScriptNode n = scriptOrFnNodes[i];
if (i == 0)
{
if (count > 1)
{
cfw.MarkTableSwitchDefault(switchStart);
switchStackTop = cfw.GetStackTop();
}
}
else
{
cfw.MarkTableSwitchCase(switchStart, i - 1, switchStackTop);
}
switch (methodIndex)
{
case Do_getFunctionName:
{
// Impelemnet method-specific switch code
// Push function name
if (n.GetType() == Token.SCRIPT)
{
cfw.AddPush(string.Empty);
}
else
{
string name = ((FunctionNode)n).GetName();
cfw.AddPush(name);
}
cfw.Add(ByteCode.ARETURN);
break;
}
case Do_getParamCount:
{
// Push number of defined parameters
cfw.AddPush(n.GetParamCount());
cfw.Add(ByteCode.IRETURN);
break;
}
case Do_getParamAndVarCount:
{
// Push number of defined parameters and declared variables
cfw.AddPush(n.GetParamAndVarCount());
cfw.Add(ByteCode.IRETURN);
break;
}
case Do_getParamOrVarName:
{
// Push name of parameter using another switch
// over paramAndVarCount
int paramAndVarCount = n.GetParamAndVarCount();
if (paramAndVarCount == 0)
{
// The runtime should never call the method in this
// case but to make bytecode verifier happy return null
// as throwing execption takes more code
cfw.Add(ByteCode.ACONST_NULL);
cfw.Add(ByteCode.ARETURN);
}
else
{
if (paramAndVarCount == 1)
{
// As above do not check for valid index but always
// return the name of the first param
cfw.AddPush(n.GetParamOrVarName(0));
cfw.Add(ByteCode.ARETURN);
}
else
{
// Do switch over getParamOrVarName
cfw.AddILoad(1);
// param or var index
// do switch from 1 .. paramAndVarCount - 1 mapping 0
// to the default case
int paramSwitchStart = cfw.AddTableSwitch(1, paramAndVarCount - 1);
for (int j = 0; j != paramAndVarCount; ++j)
{
if (cfw.GetStackTop() != 0)
{
Kit.CodeBug();
}
string s = n.GetParamOrVarName(j);
if (j == 0)
{
cfw.MarkTableSwitchDefault(paramSwitchStart);
}
else
{
cfw.MarkTableSwitchCase(paramSwitchStart, j - 1, 0);
}
cfw.AddPush(s);
cfw.Add(ByteCode.ARETURN);
}
}
}
break;
}
case Do_getParamOrVarConst:
{
// Push name of parameter using another switch
// over paramAndVarCount
paramAndVarCount = n.GetParamAndVarCount();
bool[] constness = n.GetParamAndVarConst();
if (paramAndVarCount == 0)
{
// The runtime should never call the method in this
// case but to make bytecode verifier happy return null
// as throwing execption takes more code
cfw.Add(ByteCode.ICONST_0);
cfw.Add(ByteCode.IRETURN);
}
else
{
if (paramAndVarCount == 1)
{
// As above do not check for valid index but always
// return the name of the first param
cfw.AddPush(constness[0]);
cfw.Add(ByteCode.IRETURN);
}
else
{
// Do switch over getParamOrVarName
cfw.AddILoad(1);
// param or var index
// do switch from 1 .. paramAndVarCount - 1 mapping 0
// to the default case
int paramSwitchStart = cfw.AddTableSwitch(1, paramAndVarCount - 1);
for (int j = 0; j != paramAndVarCount; ++j)
{
if (cfw.GetStackTop() != 0)
{
Kit.CodeBug();
}
if (j == 0)
{
cfw.MarkTableSwitchDefault(paramSwitchStart);
}
else
{
cfw.MarkTableSwitchCase(paramSwitchStart, j - 1, 0);
}
cfw.AddPush(constness[j]);
cfw.Add(ByteCode.IRETURN);
}
}
}
break;
}
case Do_getEncodedSource:
{
// Push number encoded source start and end
// to prepare for encodedSource.substring(start, end)
cfw.AddPush(n.GetEncodedSourceStart());
cfw.AddPush(n.GetEncodedSourceEnd());
cfw.AddInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;");
cfw.Add(ByteCode.ARETURN);
break;
}
default:
{
throw Kit.CodeBug();
}
}
}
cfw.StopMethod(methodLocals);
}
}