171 * </pre>
172 * This quickly became apparent when the code generator was generalized to work
173 * with all types, and not just numbers or objects.
174 * <p>
175 * The CodeGenerator visits nodes only once and emits bytecode for them.
176 */
177 @Logger(name="codegen")
178 final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> implements Loggable {
179
180 private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
181
182 private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
183
184 private static final Call CREATE_REWRITE_EXCEPTION = CompilerConstants.staticCallNoLookup(RewriteException.class,
185 "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class);
186 private static final Call CREATE_REWRITE_EXCEPTION_REST_OF = CompilerConstants.staticCallNoLookup(RewriteException.class,
187 "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class, int[].class);
188
189 private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
190 "ensureInt", int.class, Object.class, int.class);
191 private static final Call ENSURE_LONG = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
192 "ensureLong", long.class, Object.class, int.class);
193 private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
194 "ensureNumber", double.class, Object.class, int.class);
195
196 private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunction.class,
197 "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class);
198 private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunction.class,
199 "create", ScriptFunction.class, Object[].class, int.class);
200
201 private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
202 "toNumberForEq", double.class, Object.class);
203 private static final Call TO_NUMBER_FOR_STRICT_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
204 "toNumberForStrictEq", double.class, Object.class);
205
206
207 private static final Class<?> ITERATOR_CLASS = Iterator.class;
208 static {
209 assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
210 }
211 private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
212 private static final Type EXCEPTION_TYPE = Type.typeFor(CompilerConstants.EXCEPTION_PREFIX.type());
1709
1710 @Override
1711 public boolean enterContinueNode(final ContinueNode continueNode) {
1712 return enterJumpStatement(continueNode);
1713 }
1714
1715 @Override
1716 public boolean enterEmptyNode(final EmptyNode emptyNode) {
1717 // Don't even record the line number, it's irrelevant as there's no code.
1718 return false;
1719 }
1720
1721 @Override
1722 public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
1723 if(!method.isReachable()) {
1724 return false;
1725 }
1726 enterStatement(expressionStatement);
1727
1728 loadAndDiscard(expressionStatement.getExpression());
1729 assert method.getStackSize() == 0;
1730
1731 return false;
1732 }
1733
1734 @Override
1735 public boolean enterBlockStatement(final BlockStatement blockStatement) {
1736 if(!method.isReachable()) {
1737 return false;
1738 }
1739 enterStatement(blockStatement);
1740
1741 blockStatement.getBlock().accept(this);
1742
1743 return false;
1744 }
1745
1746 @Override
1747 public boolean enterForNode(final ForNode forNode) {
1748 if(!method.isReachable()) {
1749 return false;
2224
2225 private void lineNumber(final int lineNumber) {
2226 if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) {
2227 method.lineNumber(lineNumber);
2228 lastLineNumber = lineNumber;
2229 }
2230 }
2231
2232 int getLastLineNumber() {
2233 return lastLineNumber;
2234 }
2235
2236 /**
2237 * Load a list of nodes as an array of a specific type
2238 * The array will contain the visited nodes.
2239 *
2240 * @param arrayLiteralNode the array of contents
2241 * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT
2242 */
2243 private void loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) {
2244 assert arrayType == Type.INT_ARRAY || arrayType == Type.LONG_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY;
2245
2246 final Expression[] nodes = arrayLiteralNode.getValue();
2247 final Object presets = arrayLiteralNode.getPresets();
2248 final int[] postsets = arrayLiteralNode.getPostsets();
2249 final List<Splittable.SplitRange> ranges = arrayLiteralNode.getSplitRanges();
2250
2251 loadConstant(presets);
2252
2253 final Type elementType = arrayType.getElementType();
2254
2255 if (ranges != null) {
2256
2257 loadSplitLiteral(new SplitLiteralCreator() {
2258 @Override
2259 public void populateRange(final MethodEmitter method, final Type type, final int slot, final int start, final int end) {
2260 for (int i = start; i < end; i++) {
2261 method.load(type, slot);
2262 storeElement(nodes, elementType, postsets[i]);
2263 }
2264 method.load(type, slot);
2372 } else if (value instanceof Undefined) {
2373 method.loadUndefined(resultBounds.within(Type.OBJECT));
2374 } else if (value instanceof String) {
2375 final String string = (String)value;
2376
2377 if (string.length() > MethodEmitter.LARGE_STRING_THRESHOLD / 3) { // 3 == max bytes per encoded char
2378 loadConstant(string);
2379 } else {
2380 method.load(string);
2381 }
2382 } else if (value instanceof RegexToken) {
2383 loadRegex((RegexToken)value);
2384 } else if (value instanceof Boolean) {
2385 method.load((Boolean)value);
2386 } else if (value instanceof Integer) {
2387 if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2388 method.load((Integer)value);
2389 method.convert(Type.OBJECT);
2390 } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
2391 method.load(((Integer)value).doubleValue());
2392 } else if(!resultBounds.canBeNarrowerThan(Type.LONG)) {
2393 method.load(((Integer)value).longValue());
2394 } else {
2395 method.load((Integer)value);
2396 }
2397 } else if (value instanceof Long) {
2398 if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2399 method.load((Long)value);
2400 method.convert(Type.OBJECT);
2401 } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
2402 method.load(((Long)value).doubleValue());
2403 } else {
2404 method.load((Long)value);
2405 }
2406 } else if (value instanceof Double) {
2407 if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2408 method.load((Double)value);
2409 method.convert(Type.OBJECT);
2410 } else {
2411 method.load((Double)value);
2412 }
2413 } else if (node instanceof ArrayLiteralNode) {
2414 final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
2415 final ArrayType atype = arrayLiteral.getArrayType();
2416 loadArray(arrayLiteral, atype);
2417 globalAllocateArray(atype);
2418 } else {
2419 throw new UnsupportedOperationException("Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value);
2420 }
2421 }
2422
2423 private MethodEmitter loadRegexToken(final RegexToken value) {
2424 method.load(value.getExpression());
2425 method.load(value.getOptions());
3633 @Override
3634 protected void storeNonDiscard() {
3635 super.storeNonDiscard();
3636 if (isPostfix) {
3637 new OptimisticOperation(unaryNode, typeBounds) {
3638 @Override
3639 void loadStack() {
3640 loadMinusOne();
3641 }
3642 @Override
3643 void consumeStack() {
3644 doDecInc(getProgramPoint());
3645 }
3646 }.emit(1); // 1 for non-incremented result on the top of the stack pushed in evaluate()
3647 }
3648 }
3649
3650 private void loadMinusOne() {
3651 if (type.isInteger()) {
3652 method.load(isIncrement ? 1 : -1);
3653 } else if (type.isLong()) {
3654 method.load(isIncrement ? 1L : -1L);
3655 } else {
3656 method.load(isIncrement ? 1.0 : -1.0);
3657 }
3658 }
3659
3660 private void doDecInc(final int programPoint) {
3661 method.add(programPoint);
3662 }
3663 }.store();
3664 }
3665
3666 private static int getOptimisticIgnoreCountForSelfModifyingExpression(final Expression target) {
3667 return target instanceof AccessNode ? 1 : target instanceof IndexNode ? 2 : 0;
3668 }
3669
3670 private void loadAndDiscard(final Expression expr) {
3671 // TODO: move checks for discarding to actual expression load code (e.g. as we do with void). That way we might
3672 // be able to eliminate even more checks.
3673 if(expr instanceof PrimitiveLiteralNode | isLocalVariable(expr)) {
3674 assert !lc.isCurrentDiscard(expr);
4016
4017 private void loadASSIGN_SAR(final BinaryNode binaryNode) {
4018 new BinarySelfAssignment(binaryNode) {
4019 @Override
4020 protected void op() {
4021 method.sar();
4022 }
4023 }.store();
4024 }
4025
4026 private void loadASSIGN_SHL(final BinaryNode binaryNode) {
4027 new BinarySelfAssignment(binaryNode) {
4028 @Override
4029 protected void op() {
4030 method.shl();
4031 }
4032 }.store();
4033 }
4034
4035 private void loadASSIGN_SHR(final BinaryNode binaryNode) {
4036 new BinarySelfAssignment(binaryNode) {
4037 @Override
4038 protected void op() {
4039 doSHR();
4040 }
4041
4042 }.store();
4043 }
4044
4045 private void doSHR() {
4046 // TODO: make SHR optimistic
4047 method.shr();
4048 toUint();
4049 }
4050
4051 private void toUint() {
4052 JSType.TO_UINT32_I.invoke(method);
4053 }
4054
4055 private void loadASSIGN_SUB(final BinaryNode binaryNode) {
4056 new BinaryOptimisticSelfAssignment(binaryNode) {
4057 @Override
4058 protected void op(final OptimisticOperation oo) {
4059 method.sub(oo.getProgramPoint());
4060 }
4061 }.store();
4062 }
4063
4064 /**
4065 * Helper class for binary arithmetic ops
4066 */
4067 private abstract class BinaryArith {
4068 protected abstract void op(int programPoint);
4069
4070 protected void evaluate(final BinaryNode node, final TypeBounds resultBounds) {
4071 final TypeBounds numericBounds = resultBounds.booleanToInt().objectToNumber();
4072 new OptimisticOperation(node, numericBounds) {
4073 @Override
4074 void loadStack() {
4075 final TypeBounds operandBounds;
4076 boolean forceConversionSeparation = false;
4077 if(numericBounds.narrowest == Type.NUMBER) {
4078 // Result should be double always. Propagate it into the operands so we don't have lots of I2D
4079 // and L2D after operand evaluation.
4080 assert numericBounds.widest == Type.NUMBER;
4081 operandBounds = numericBounds;
4082 } else {
4083 final boolean isOptimistic = isValid(getProgramPoint());
4084 if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
4085 operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
4086 } else {
4087 // Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
4088 operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
4089 numericBounds.widest), Type.NUMBER);
4090 forceConversionSeparation = node.getWidestOperationType().narrowerThan(numericBounds.widest);
4091 }
4092 }
4093 loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false, forceConversionSeparation);
4094 }
4095
4096 @Override
4097 void consumeStack() {
4098 op(getProgramPoint());
4099 }
4100 }.emit();
4101 }
4102 }
4103
4104 private void loadBIT_AND(final BinaryNode binaryNode) {
4105 loadBinaryOperands(binaryNode);
4106 method.and();
4107 }
4108
4109 private void loadBIT_OR(final BinaryNode binaryNode) {
4110 // Optimize x|0 to (int)x
4172 private void loadMUL(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4173 new BinaryArith() {
4174 @Override
4175 protected void op(final int programPoint) {
4176 method.mul(programPoint);
4177 }
4178 }.evaluate(binaryNode, resultBounds);
4179 }
4180
4181 private void loadSAR(final BinaryNode binaryNode) {
4182 loadBinaryOperands(binaryNode);
4183 method.sar();
4184 }
4185
4186 private void loadSHL(final BinaryNode binaryNode) {
4187 loadBinaryOperands(binaryNode);
4188 method.shl();
4189 }
4190
4191 private void loadSHR(final BinaryNode binaryNode) {
4192 // Optimize x >>> 0 to (uint)x
4193 if (isRhsZero(binaryNode)) {
4194 loadExpressionAsType(binaryNode.lhs(), Type.INT);
4195 toUint();
4196 } else {
4197 loadBinaryOperands(binaryNode);
4198 doSHR();
4199 }
4200 }
4201
4202 private void loadSUB(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4203 new BinaryArith() {
4204 @Override
4205 protected void op(final int programPoint) {
4206 method.sub(programPoint);
4207 }
4208 }.evaluate(binaryNode, resultBounds);
4209 }
4210
4211 @Override
4212 public boolean enterLabelNode(final LabelNode labelNode) {
4213 labeledBlockBreakLiveLocals.push(lc.getUsedSlotCount());
4214 return true;
4215 }
4216
4217 @Override
4218 protected boolean enterDefault(final Node node) {
4219 throw new AssertionError("Code generator entered node of type " + node.getClass().getName());
4450 */
4451 target.accept(new SimpleNodeVisitor() {
4452 @Override
4453 protected boolean enterDefault(final Node node) {
4454 throw new AssertionError("Unexpected node " + node + " in store epilogue");
4455 }
4456
4457 @Override
4458 public boolean enterIdentNode(final IdentNode node) {
4459 final Symbol symbol = node.getSymbol();
4460 assert symbol != null;
4461 if (symbol.isScope()) {
4462 final int flags = getScopeCallSiteFlags(symbol);
4463 if (isFastScope(symbol)) {
4464 storeFastScopeVar(symbol, flags);
4465 } else {
4466 method.dynamicSet(node.getName(), flags, false);
4467 }
4468 } else {
4469 final Type storeType = assignNode.getType();
4470 if (symbol.hasSlotFor(storeType)) {
4471 // Only emit a convert for a store known to be live; converts for dead stores can
4472 // give us an unnecessary ClassCastException.
4473 method.convert(storeType);
4474 }
4475 storeIdentWithCatchConversion(node, storeType);
4476 }
4477 return false;
4478
4479 }
4480
4481 @Override
4482 public boolean enterAccessNode(final AccessNode node) {
4483 method.dynamicSet(node.getProperty(), getCallSiteFlags(), node.isIndex());
4484 return false;
4485 }
4486
4487 @Override
4488 public boolean enterIndexNode(final IndexNode node) {
4489 method.dynamicSetIndex(getCallSiteFlags());
4834 return method.dynamicCall(getOptimisticCoercedType(), argCount, getOptimisticFlags(flags), msg);
4835 }
4836 return method.dynamicCall(resultBounds.within(expression.getType()), argCount, nonOptimisticFlags(flags), msg);
4837 }
4838
4839 int getOptimisticFlags(final int flags) {
4840 return flags | CALLSITE_OPTIMISTIC | (optimistic.getProgramPoint() << CALLSITE_PROGRAM_POINT_SHIFT); //encode program point in high bits
4841 }
4842
4843 int getProgramPoint() {
4844 return isOptimistic ? optimistic.getProgramPoint() : INVALID_PROGRAM_POINT;
4845 }
4846
4847 void convertOptimisticReturnValue() {
4848 if (isOptimistic) {
4849 final Type optimisticType = getOptimisticCoercedType();
4850 if(!optimisticType.isObject()) {
4851 method.load(optimistic.getProgramPoint());
4852 if(optimisticType.isInteger()) {
4853 method.invoke(ENSURE_INT);
4854 } else if(optimisticType.isLong()) {
4855 method.invoke(ENSURE_LONG);
4856 } else if(optimisticType.isNumber()) {
4857 method.invoke(ENSURE_NUMBER);
4858 } else {
4859 throw new AssertionError(optimisticType);
4860 }
4861 }
4862 }
4863 }
4864
4865 void replaceCompileTimeProperty() {
4866 final IdentNode identNode = (IdentNode)expression;
4867 final String name = identNode.getSymbol().getName();
4868 if (CompilerConstants.__FILE__.name().equals(name)) {
4869 replaceCompileTimeProperty(getCurrentSource().getName());
4870 } else if (CompilerConstants.__DIR__.name().equals(name)) {
4871 replaceCompileTimeProperty(getCurrentSource().getBase());
4872 } else if (CompilerConstants.__LINE__.name().equals(name)) {
4873 replaceCompileTimeProperty(getCurrentSource().getLine(identNode.position()));
4874 }
4875 }
|
171 * </pre>
172 * This quickly became apparent when the code generator was generalized to work
173 * with all types, and not just numbers or objects.
174 * <p>
175 * The CodeGenerator visits nodes only once and emits bytecode for them.
176 */
177 @Logger(name="codegen")
178 final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContext> implements Loggable {
179
180 private static final Type SCOPE_TYPE = Type.typeFor(ScriptObject.class);
181
182 private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class);
183
184 private static final Call CREATE_REWRITE_EXCEPTION = CompilerConstants.staticCallNoLookup(RewriteException.class,
185 "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class);
186 private static final Call CREATE_REWRITE_EXCEPTION_REST_OF = CompilerConstants.staticCallNoLookup(RewriteException.class,
187 "create", RewriteException.class, UnwarrantedOptimismException.class, Object[].class, String[].class, int[].class);
188
189 private static final Call ENSURE_INT = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
190 "ensureInt", int.class, Object.class, int.class);
191 private static final Call ENSURE_NUMBER = CompilerConstants.staticCallNoLookup(OptimisticReturnFilters.class,
192 "ensureNumber", double.class, Object.class, int.class);
193
194 private static final Call CREATE_FUNCTION_OBJECT = CompilerConstants.staticCallNoLookup(ScriptFunction.class,
195 "create", ScriptFunction.class, Object[].class, int.class, ScriptObject.class);
196 private static final Call CREATE_FUNCTION_OBJECT_NO_SCOPE = CompilerConstants.staticCallNoLookup(ScriptFunction.class,
197 "create", ScriptFunction.class, Object[].class, int.class);
198
199 private static final Call TO_NUMBER_FOR_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
200 "toNumberForEq", double.class, Object.class);
201 private static final Call TO_NUMBER_FOR_STRICT_EQ = CompilerConstants.staticCallNoLookup(JSType.class,
202 "toNumberForStrictEq", double.class, Object.class);
203
204
205 private static final Class<?> ITERATOR_CLASS = Iterator.class;
206 static {
207 assert ITERATOR_CLASS == CompilerConstants.ITERATOR_PREFIX.type();
208 }
209 private static final Type ITERATOR_TYPE = Type.typeFor(ITERATOR_CLASS);
210 private static final Type EXCEPTION_TYPE = Type.typeFor(CompilerConstants.EXCEPTION_PREFIX.type());
1707
1708 @Override
1709 public boolean enterContinueNode(final ContinueNode continueNode) {
1710 return enterJumpStatement(continueNode);
1711 }
1712
1713 @Override
1714 public boolean enterEmptyNode(final EmptyNode emptyNode) {
1715 // Don't even record the line number, it's irrelevant as there's no code.
1716 return false;
1717 }
1718
1719 @Override
1720 public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
1721 if(!method.isReachable()) {
1722 return false;
1723 }
1724 enterStatement(expressionStatement);
1725
1726 loadAndDiscard(expressionStatement.getExpression());
1727 assert method.getStackSize() == 0 : "stack not empty in " + expressionStatement;
1728
1729 return false;
1730 }
1731
1732 @Override
1733 public boolean enterBlockStatement(final BlockStatement blockStatement) {
1734 if(!method.isReachable()) {
1735 return false;
1736 }
1737 enterStatement(blockStatement);
1738
1739 blockStatement.getBlock().accept(this);
1740
1741 return false;
1742 }
1743
1744 @Override
1745 public boolean enterForNode(final ForNode forNode) {
1746 if(!method.isReachable()) {
1747 return false;
2222
2223 private void lineNumber(final int lineNumber) {
2224 if (lineNumber != lastLineNumber && lineNumber != Node.NO_LINE_NUMBER) {
2225 method.lineNumber(lineNumber);
2226 lastLineNumber = lineNumber;
2227 }
2228 }
2229
2230 int getLastLineNumber() {
2231 return lastLineNumber;
2232 }
2233
2234 /**
2235 * Load a list of nodes as an array of a specific type
2236 * The array will contain the visited nodes.
2237 *
2238 * @param arrayLiteralNode the array of contents
2239 * @param arrayType the type of the array, e.g. ARRAY_NUMBER or ARRAY_OBJECT
2240 */
2241 private void loadArray(final ArrayLiteralNode arrayLiteralNode, final ArrayType arrayType) {
2242 assert arrayType == Type.INT_ARRAY || arrayType == Type.NUMBER_ARRAY || arrayType == Type.OBJECT_ARRAY;
2243
2244 final Expression[] nodes = arrayLiteralNode.getValue();
2245 final Object presets = arrayLiteralNode.getPresets();
2246 final int[] postsets = arrayLiteralNode.getPostsets();
2247 final List<Splittable.SplitRange> ranges = arrayLiteralNode.getSplitRanges();
2248
2249 loadConstant(presets);
2250
2251 final Type elementType = arrayType.getElementType();
2252
2253 if (ranges != null) {
2254
2255 loadSplitLiteral(new SplitLiteralCreator() {
2256 @Override
2257 public void populateRange(final MethodEmitter method, final Type type, final int slot, final int start, final int end) {
2258 for (int i = start; i < end; i++) {
2259 method.load(type, slot);
2260 storeElement(nodes, elementType, postsets[i]);
2261 }
2262 method.load(type, slot);
2370 } else if (value instanceof Undefined) {
2371 method.loadUndefined(resultBounds.within(Type.OBJECT));
2372 } else if (value instanceof String) {
2373 final String string = (String)value;
2374
2375 if (string.length() > MethodEmitter.LARGE_STRING_THRESHOLD / 3) { // 3 == max bytes per encoded char
2376 loadConstant(string);
2377 } else {
2378 method.load(string);
2379 }
2380 } else if (value instanceof RegexToken) {
2381 loadRegex((RegexToken)value);
2382 } else if (value instanceof Boolean) {
2383 method.load((Boolean)value);
2384 } else if (value instanceof Integer) {
2385 if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2386 method.load((Integer)value);
2387 method.convert(Type.OBJECT);
2388 } else if(!resultBounds.canBeNarrowerThan(Type.NUMBER)) {
2389 method.load(((Integer)value).doubleValue());
2390 } else {
2391 method.load((Integer)value);
2392 }
2393 } else if (value instanceof Double) {
2394 if(!resultBounds.canBeNarrowerThan(Type.OBJECT)) {
2395 method.load((Double)value);
2396 method.convert(Type.OBJECT);
2397 } else {
2398 method.load((Double)value);
2399 }
2400 } else if (node instanceof ArrayLiteralNode) {
2401 final ArrayLiteralNode arrayLiteral = (ArrayLiteralNode)node;
2402 final ArrayType atype = arrayLiteral.getArrayType();
2403 loadArray(arrayLiteral, atype);
2404 globalAllocateArray(atype);
2405 } else {
2406 throw new UnsupportedOperationException("Unknown literal for " + node.getClass() + " " + value.getClass() + " " + value);
2407 }
2408 }
2409
2410 private MethodEmitter loadRegexToken(final RegexToken value) {
2411 method.load(value.getExpression());
2412 method.load(value.getOptions());
3620 @Override
3621 protected void storeNonDiscard() {
3622 super.storeNonDiscard();
3623 if (isPostfix) {
3624 new OptimisticOperation(unaryNode, typeBounds) {
3625 @Override
3626 void loadStack() {
3627 loadMinusOne();
3628 }
3629 @Override
3630 void consumeStack() {
3631 doDecInc(getProgramPoint());
3632 }
3633 }.emit(1); // 1 for non-incremented result on the top of the stack pushed in evaluate()
3634 }
3635 }
3636
3637 private void loadMinusOne() {
3638 if (type.isInteger()) {
3639 method.load(isIncrement ? 1 : -1);
3640 } else {
3641 method.load(isIncrement ? 1.0 : -1.0);
3642 }
3643 }
3644
3645 private void doDecInc(final int programPoint) {
3646 method.add(programPoint);
3647 }
3648 }.store();
3649 }
3650
3651 private static int getOptimisticIgnoreCountForSelfModifyingExpression(final Expression target) {
3652 return target instanceof AccessNode ? 1 : target instanceof IndexNode ? 2 : 0;
3653 }
3654
3655 private void loadAndDiscard(final Expression expr) {
3656 // TODO: move checks for discarding to actual expression load code (e.g. as we do with void). That way we might
3657 // be able to eliminate even more checks.
3658 if(expr instanceof PrimitiveLiteralNode | isLocalVariable(expr)) {
3659 assert !lc.isCurrentDiscard(expr);
4001
4002 private void loadASSIGN_SAR(final BinaryNode binaryNode) {
4003 new BinarySelfAssignment(binaryNode) {
4004 @Override
4005 protected void op() {
4006 method.sar();
4007 }
4008 }.store();
4009 }
4010
4011 private void loadASSIGN_SHL(final BinaryNode binaryNode) {
4012 new BinarySelfAssignment(binaryNode) {
4013 @Override
4014 protected void op() {
4015 method.shl();
4016 }
4017 }.store();
4018 }
4019
4020 private void loadASSIGN_SHR(final BinaryNode binaryNode) {
4021 new SelfModifyingStore<BinaryNode>(binaryNode, binaryNode.lhs()) {
4022 @Override
4023 protected void evaluate() {
4024 new OptimisticOperation(assignNode, new TypeBounds(Type.INT, Type.NUMBER)) {
4025 @Override
4026 void loadStack() {
4027 assert assignNode.getWidestOperandType() == Type.INT;
4028 if (isRhsZero(binaryNode)) {
4029 loadExpressionAsType(binaryNode.lhs(), Type.INT);
4030 } else {
4031 loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), TypeBounds.INT, true, false);
4032 method.shr();
4033 }
4034 }
4035
4036 @Override
4037 void consumeStack() {
4038 if (isOptimistic(binaryNode)) {
4039 toUint32Optimistic(binaryNode.getProgramPoint());
4040 } else {
4041 toUint32Double();
4042 }
4043 }
4044 }.emit(getOptimisticIgnoreCountForSelfModifyingExpression(binaryNode.lhs()));
4045 method.convert(assignNode.getType());
4046 }
4047 }.store();
4048 }
4049
4050 private void doSHR(final BinaryNode binaryNode) {
4051 new OptimisticOperation(binaryNode, new TypeBounds(Type.INT, Type.NUMBER)) {
4052 @Override
4053 void loadStack() {
4054 if (isRhsZero(binaryNode)) {
4055 loadExpressionAsType(binaryNode.lhs(), Type.INT);
4056 } else {
4057 loadBinaryOperands(binaryNode);
4058 method.shr();
4059 }
4060 }
4061
4062 @Override
4063 void consumeStack() {
4064 if (isOptimistic(binaryNode)) {
4065 toUint32Optimistic(binaryNode.getProgramPoint());
4066 } else {
4067 toUint32Double();
4068 }
4069 }
4070 }.emit();
4071
4072 }
4073
4074 private void toUint32Optimistic(final int programPoint) {
4075 method.load(programPoint);
4076 JSType.TO_UINT32_OPTIMISTIC.invoke(method);
4077 }
4078
4079 private void toUint32Double() {
4080 JSType.TO_UINT32_DOUBLE.invoke(method);
4081 }
4082
4083 private void loadASSIGN_SUB(final BinaryNode binaryNode) {
4084 new BinaryOptimisticSelfAssignment(binaryNode) {
4085 @Override
4086 protected void op(final OptimisticOperation oo) {
4087 method.sub(oo.getProgramPoint());
4088 }
4089 }.store();
4090 }
4091
4092 /**
4093 * Helper class for binary arithmetic ops
4094 */
4095 private abstract class BinaryArith {
4096 protected abstract void op(int programPoint);
4097
4098 protected void evaluate(final BinaryNode node, final TypeBounds resultBounds) {
4099 final TypeBounds numericBounds = resultBounds.booleanToInt().objectToNumber();
4100 new OptimisticOperation(node, numericBounds) {
4101 @Override
4102 void loadStack() {
4103 final TypeBounds operandBounds;
4104 boolean forceConversionSeparation = false;
4105 if(numericBounds.narrowest == Type.NUMBER) {
4106 // Result should be double always. Propagate it into the operands so we don't have lots of I2D
4107 // and L2D after operand evaluation.
4108 assert numericBounds.widest == Type.NUMBER;
4109 operandBounds = numericBounds;
4110 } else {
4111 final boolean isOptimistic = isValid(getProgramPoint());
4112 if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
4113 operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
4114 } else {
4115 // Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
4116 operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
4117 numericBounds.widest), Type.NUMBER);
4118 forceConversionSeparation = true;
4119 }
4120 }
4121 loadBinaryOperands(node.lhs(), node.rhs(), operandBounds, false, forceConversionSeparation);
4122 }
4123
4124 @Override
4125 void consumeStack() {
4126 op(getProgramPoint());
4127 }
4128 }.emit();
4129 }
4130 }
4131
4132 private void loadBIT_AND(final BinaryNode binaryNode) {
4133 loadBinaryOperands(binaryNode);
4134 method.and();
4135 }
4136
4137 private void loadBIT_OR(final BinaryNode binaryNode) {
4138 // Optimize x|0 to (int)x
4200 private void loadMUL(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4201 new BinaryArith() {
4202 @Override
4203 protected void op(final int programPoint) {
4204 method.mul(programPoint);
4205 }
4206 }.evaluate(binaryNode, resultBounds);
4207 }
4208
4209 private void loadSAR(final BinaryNode binaryNode) {
4210 loadBinaryOperands(binaryNode);
4211 method.sar();
4212 }
4213
4214 private void loadSHL(final BinaryNode binaryNode) {
4215 loadBinaryOperands(binaryNode);
4216 method.shl();
4217 }
4218
4219 private void loadSHR(final BinaryNode binaryNode) {
4220 doSHR(binaryNode);
4221 }
4222
4223 private void loadSUB(final BinaryNode binaryNode, final TypeBounds resultBounds) {
4224 new BinaryArith() {
4225 @Override
4226 protected void op(final int programPoint) {
4227 method.sub(programPoint);
4228 }
4229 }.evaluate(binaryNode, resultBounds);
4230 }
4231
4232 @Override
4233 public boolean enterLabelNode(final LabelNode labelNode) {
4234 labeledBlockBreakLiveLocals.push(lc.getUsedSlotCount());
4235 return true;
4236 }
4237
4238 @Override
4239 protected boolean enterDefault(final Node node) {
4240 throw new AssertionError("Code generator entered node of type " + node.getClass().getName());
4471 */
4472 target.accept(new SimpleNodeVisitor() {
4473 @Override
4474 protected boolean enterDefault(final Node node) {
4475 throw new AssertionError("Unexpected node " + node + " in store epilogue");
4476 }
4477
4478 @Override
4479 public boolean enterIdentNode(final IdentNode node) {
4480 final Symbol symbol = node.getSymbol();
4481 assert symbol != null;
4482 if (symbol.isScope()) {
4483 final int flags = getScopeCallSiteFlags(symbol);
4484 if (isFastScope(symbol)) {
4485 storeFastScopeVar(symbol, flags);
4486 } else {
4487 method.dynamicSet(node.getName(), flags, false);
4488 }
4489 } else {
4490 final Type storeType = assignNode.getType();
4491 assert storeType != Type.LONG;
4492 if (symbol.hasSlotFor(storeType)) {
4493 // Only emit a convert for a store known to be live; converts for dead stores can
4494 // give us an unnecessary ClassCastException.
4495 method.convert(storeType);
4496 }
4497 storeIdentWithCatchConversion(node, storeType);
4498 }
4499 return false;
4500
4501 }
4502
4503 @Override
4504 public boolean enterAccessNode(final AccessNode node) {
4505 method.dynamicSet(node.getProperty(), getCallSiteFlags(), node.isIndex());
4506 return false;
4507 }
4508
4509 @Override
4510 public boolean enterIndexNode(final IndexNode node) {
4511 method.dynamicSetIndex(getCallSiteFlags());
4856 return method.dynamicCall(getOptimisticCoercedType(), argCount, getOptimisticFlags(flags), msg);
4857 }
4858 return method.dynamicCall(resultBounds.within(expression.getType()), argCount, nonOptimisticFlags(flags), msg);
4859 }
4860
4861 int getOptimisticFlags(final int flags) {
4862 return flags | CALLSITE_OPTIMISTIC | (optimistic.getProgramPoint() << CALLSITE_PROGRAM_POINT_SHIFT); //encode program point in high bits
4863 }
4864
4865 int getProgramPoint() {
4866 return isOptimistic ? optimistic.getProgramPoint() : INVALID_PROGRAM_POINT;
4867 }
4868
4869 void convertOptimisticReturnValue() {
4870 if (isOptimistic) {
4871 final Type optimisticType = getOptimisticCoercedType();
4872 if(!optimisticType.isObject()) {
4873 method.load(optimistic.getProgramPoint());
4874 if(optimisticType.isInteger()) {
4875 method.invoke(ENSURE_INT);
4876 } else if(optimisticType.isNumber()) {
4877 method.invoke(ENSURE_NUMBER);
4878 } else {
4879 throw new AssertionError(optimisticType);
4880 }
4881 }
4882 }
4883 }
4884
4885 void replaceCompileTimeProperty() {
4886 final IdentNode identNode = (IdentNode)expression;
4887 final String name = identNode.getSymbol().getName();
4888 if (CompilerConstants.__FILE__.name().equals(name)) {
4889 replaceCompileTimeProperty(getCurrentSource().getName());
4890 } else if (CompilerConstants.__DIR__.name().equals(name)) {
4891 replaceCompileTimeProperty(getCurrentSource().getBase());
4892 } else if (CompilerConstants.__LINE__.name().equals(name)) {
4893 replaceCompileTimeProperty(getCurrentSource().getLine(identNode.position()));
4894 }
4895 }
|