/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.compiler.assembler; import java.lang.reflect.Modifier; import com.github.anba.es6draft.compiler.assembler.Code.ClassCode; import com.github.anba.es6draft.compiler.assembler.Code.MethodCode; /** * Concrete {@link ConstantPool} implementation which handles all constant values as extern * constants, i.e. the constant value is retrieved from an external class. */ final class ExternConstantPool extends ConstantPool { private static final int EXTERN_CONSTANTS_LIMIT = 0x6000; private static final int METHOD_LIMIT = 0x400; private static final class TypeSpec<T> { final String methodName; final MethodTypeDescriptor methodDescriptor; final T defaultValue; private TypeSpec(String methodName, Type type, T defaultValue) { this.methodName = methodName; this.methodDescriptor = MethodTypeDescriptor.methodType(type, Type.INT_TYPE); this.defaultValue = defaultValue; } String methodName(int index) { if (index < 0) { return methodName; } return methodName + "_" + index; } static final TypeSpec<Integer> INT = new TypeSpec<>("getInt", Type.INT_TYPE, 0); static final TypeSpec<Long> LONG = new TypeSpec<>("getLong", Type.LONG_TYPE, 0L); static final TypeSpec<Float> FLOAT = new TypeSpec<>("getFloat", Type.FLOAT_TYPE, 0f); static final TypeSpec<Double> DOUBLE = new TypeSpec<>("getDouble", Type.DOUBLE_TYPE, 0d); static final TypeSpec<String> STRING = new TypeSpec<>("getString", Types.String, ""); } private static final class ConstantClassAssembler extends InstructionAssembler { public ConstantClassAssembler(MethodCode method) { super(method); } @Override protected Stack createStack(Variables variables) { return new EmptyStack(variables); } } private final ClassCode classCode; private boolean closed = false; ExternConstantPool(Code code) { super(code, EXTERN_CONSTANTS_LIMIT); classCode = code.newClass(this); } @Override protected ConstantPool newConstantPool() { return new ExternConstantPool(code); } @Override protected void close() { assert !closed && (closed = true) : "constant pool closed"; generate(TypeSpec.INT, getIntegers()); generate(TypeSpec.LONG, getLongs()); generate(TypeSpec.FLOAT, getFloats()); generate(TypeSpec.DOUBLE, getDoubles()); generate(TypeSpec.STRING, getStrings()); } private <T> void generate(TypeSpec<T> spec, T[] values) { if (values.length <= METHOD_LIMIT) { generate(spec, -1, values, 0, values.length); } else { generateSplit(spec, values); } } private <T> void generateSplit(TypeSpec<T> spec, T[] values) { // split into multiple methods for (int i = 0, index = 0; i < values.length; i += METHOD_LIMIT, ++index) { int from = i, to = Math.min(from + METHOD_LIMIT, values.length); generate(spec, index, values, from, to); } // generate entry method InstructionAssembler asm = newMethod(spec.methodName, spec); asm.begin(); for (int i = 0, index = 0; i < values.length; i += METHOD_LIMIT, ++index) { int from = i, to = Math.min(from + METHOD_LIMIT, values.length); Jump jump = new Jump(); asm.loadParameter(0, Type.INT_TYPE); asm.sipush(to); asm.ificmpge(jump); asm.loadParameter(0, Type.INT_TYPE); asm.invokestatic(classCode.classType, spec.methodName(index), spec.methodDescriptor, false); asm._return(); asm.mark(jump); } asm.ldc(spec.defaultValue); asm._return(); asm.end(); } private <T> void generate(TypeSpec<T> spec, int index, T[] values, int from, int to) { int length = to - from; if (length == 0) { return; } Jump dflt = new Jump(); Jump[] jumps = new Jump[length]; for (int caseIndex = 0; caseIndex < length; ++caseIndex) { jumps[caseIndex] = new Jump(); } InstructionAssembler asm = newMethod(spec.methodName(index), spec); asm.begin(); asm.loadParameter(0, Type.INT_TYPE); asm.tableswitch(from, to - 1, dflt, jumps); for (int caseIndex = 0; caseIndex < length; ++caseIndex) { asm.mark(jumps[caseIndex]); asm.ldc(values[from + caseIndex]); asm._return(); } asm.mark(dflt); asm.ldc(spec.defaultValue); asm._return(); asm.end(); } private <T> ConstantClassAssembler newMethod(String methodName, TypeSpec<T> spec) { MethodCode method = classCode.newMethod(Modifier.PUBLIC | Modifier.STATIC, methodName, spec.methodDescriptor, null, null); return new ConstantClassAssembler(method); } private void load(InstructionAssembler assembler, int index, TypeSpec<?> spec) { assert 0 <= index && index <= EXTERN_CONSTANTS_LIMIT; assembler.iconst(index); assembler.invokestatic(classCode.classType, spec.methodName, spec.methodDescriptor, false); } @Override protected void iconst(InstructionAssembler assembler, Integer cst, int index) { load(assembler, index, TypeSpec.INT); } @Override protected void lconst(InstructionAssembler assembler, Long cst, int index) { load(assembler, index, TypeSpec.LONG); } @Override protected void fconst(InstructionAssembler assembler, Float cst, int index) { load(assembler, index, TypeSpec.FLOAT); } @Override protected void dconst(InstructionAssembler assembler, Double cst, int index) { load(assembler, index, TypeSpec.DOUBLE); } @Override protected void aconst(InstructionAssembler assembler, String cst, int index) { load(assembler, index, TypeSpec.STRING); } }