in rhino/src/main/java/org/mozilla/javascript/optimizer/Codegen.java [719:977]
private void generateNativeFunctionOverrides(ClassFileWriter cfw, String rawSource) {
// Override NativeFunction.getLanguageVersion() with
// public int getLanguageVersion() { return <version-constant>; }
cfw.startMethod("getLanguageVersion", "()I", 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
final int Do_getFunctionName = 0;
final int Do_getParamCount = 1;
final int Do_getParamAndVarCount = 2;
final int Do_getParamOrVarName = 3;
final int Do_getRawSource = 4;
final int Do_getParamOrVarConst = 5;
final int Do_isGeneratorFunction = 6;
final int Do_hasRestParameter = 7;
final int Do_hasDefaultParameters = 8;
final int Do_isStrict = 9;
final int SWITCH_COUNT = 10;
for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
if (methodIndex == Do_getRawSource && rawSource == 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;", ACC_PUBLIC);
break;
case Do_getParamCount:
methodLocals = 1; // Only this
cfw.startMethod("getParamCount", "()I", ACC_PUBLIC);
break;
case Do_getParamAndVarCount:
methodLocals = 1; // Only this
cfw.startMethod("getParamAndVarCount", "()I", ACC_PUBLIC);
break;
case Do_getParamOrVarName:
methodLocals = 1 + 1; // this + paramOrVarIndex
cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;", ACC_PUBLIC);
break;
case Do_getParamOrVarConst:
methodLocals = 1 + 1 + 1; // this + paramOrVarName
cfw.startMethod("getParamOrVarConst", "(I)Z", ACC_PUBLIC);
break;
case Do_getRawSource:
methodLocals = 1; // Only this
cfw.startMethod("getRawSource", "()Ljava/lang/String;", ACC_PUBLIC);
cfw.addPush(rawSource);
break;
case Do_isGeneratorFunction:
methodLocals = 1; // Only this
cfw.startMethod("isGeneratorFunction", "()Z", ACC_PROTECTED);
break;
case Do_hasRestParameter:
methodLocals = 1; // Only this
cfw.startMethod("hasRestParameter", "()Z", ACC_PUBLIC);
break;
case Do_hasDefaultParameters:
methodLocals = 1; // Only this
cfw.startMethod("hasDefaultParameters", "()Z", ACC_PUBLIC);
break;
case Do_isStrict:
methodLocals = 1; // Only this
cfw.startMethod("isStrict", "()Z", ACC_PUBLIC);
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);
}
// Impelemnet method-specific switch code
switch (methodIndex) {
case Do_getFunctionName:
// Push function name
if (n.getType() == Token.SCRIPT) {
cfw.addPush("");
} else {
String name = ((FunctionNode) n).getName();
cfw.addPush(name);
}
cfw.add(ByteCode.ARETURN);
break;
case Do_getParamCount:
// Push number of defined parameters
if (n.hasRestParameter()) {
cfw.addPush(n.getParamCount() - 1);
} else {
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();
boolean[] 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_isGeneratorFunction:
// Push a boolean if it's a generator
if (n instanceof FunctionNode) {
cfw.addPush(((FunctionNode) n).isES6Generator());
} else {
cfw.addPush(false);
}
cfw.add(ByteCode.IRETURN);
break;
case Do_hasRestParameter:
// Push boolean of defined hasRestParameter
cfw.addPush(n.hasRestParameter());
cfw.add(ByteCode.IRETURN);
break;
case Do_hasDefaultParameters:
if (n instanceof FunctionNode) {
cfw.addPush(n.getDefaultParams() != null);
} else {
cfw.addPush(false);
}
cfw.add(ByteCode.IRETURN);
break;
case Do_getRawSource:
// Push number raw source start and end
// to prepare for rawSource.substring(start, end)
cfw.addPush(n.getRawSourceStart());
cfw.addPush(n.getRawSourceEnd());
cfw.addInvoke(
ByteCode.INVOKEVIRTUAL,
"java/lang/String",
"substring",
"(II)Ljava/lang/String;");
cfw.add(ByteCode.ARETURN);
break;
case Do_isStrict:
cfw.addPush(n.isInStrictMode() ? 1 : 0);
cfw.add(ByteCode.IRETURN);
break;
default:
throw Kit.codeBug();
}
}
cfw.stopMethod(methodLocals);
}
}