private void generateNativeFunctionOverrides()

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);
        }
    }