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