240 private final DebugLogger log;
241
242 /** From what size should we use spill instead of fields for JavaScript objects? */
243 static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
244
245 private final Set<String> emittedMethods = new HashSet<>();
246
247 // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
248 private ContinuationInfo continuationInfo;
249
250 private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
251
252 private static final Label METHOD_BOUNDARY = new Label("");
253 private final Deque<Label> catchLabels = new ArrayDeque<>();
254 // Number of live locals on entry to (and thus also break from) labeled blocks.
255 private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
256
257 //is this a rest of compilation
258 private final int[] continuationEntryPoints;
259
260 /**
261 * Constructor.
262 *
263 * @param compiler
264 */
265 CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
266 super(new CodeGeneratorLexicalContext());
267 this.compiler = compiler;
268 this.evalCode = compiler.getSource().isEvalCode();
269 this.continuationEntryPoints = continuationEntryPoints;
270 this.callSiteFlags = compiler.getScriptEnvironment()._callsite_flags;
271 this.log = initLogger(compiler.getContext());
272 }
273
274 @Override
275 public DebugLogger getLogger() {
276 return log;
277 }
278
279 @Override
1261 method.breakLabel(entryLabel, lc.getUsedSlotCount());
1262 } else {
1263 method.label(entryLabel);
1264 }
1265 if(!method.isReachable()) {
1266 return false;
1267 }
1268 if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
1269 return false;
1270 }
1271 initLocals(block);
1272
1273 assert lc.getUsedSlotCount() == method.getFirstTemp();
1274 return true;
1275 }
1276
1277 boolean useOptimisticTypes() {
1278 return !lc.inSplitNode() && compiler.useOptimisticTypes();
1279 }
1280
1281 @Override
1282 public Node leaveBlock(final Block block) {
1283 popBlockScope(block);
1284 method.beforeJoinPoint(block);
1285
1286 closeBlockVariables(block);
1287 lc.releaseSlots();
1288 assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1289 "reachable="+method.isReachable() +
1290 " isFunctionBody=" + lc.isFunctionBody() +
1291 " usedSlotCount=" + lc.getUsedSlotCount() +
1292 " firstTemp=" + method.getFirstTemp();
1293
1294 return block;
1295 }
1296
1297 private void popBlockScope(final Block block) {
1298 final Label breakLabel = block.getBreakLabel();
1299
1300 if(!block.needsScope() || lc.isFunctionBody()) {
1301 emitBlockBreakLabel(breakLabel);
1302 return;
1303 }
1304
1305 final Label beginTryLabel = scopeEntryLabels.pop();
1306 final Label recoveryLabel = new Label("block_popscope_catch");
1307 emitBlockBreakLabel(breakLabel);
1308 final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1309 if(bodyCanThrow) {
1310 method._try(beginTryLabel, breakLabel, recoveryLabel);
1311 }
1312
1313 Label afterCatchLabel = null;
1314
1315 if(method.isReachable()) {
1316 popScope();
1317 if(bodyCanThrow) {
1318 afterCatchLabel = new Label("block_after_catch");
1319 method._goto(afterCatchLabel);
1795 }
1796
1797 @Override
1798 protected void evaluate() {
1799 new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
1800 @Override
1801 void loadStack() {
1802 method.load(ITERATOR_TYPE, iterSlot);
1803 }
1804
1805 @Override
1806 void consumeStack() {
1807 method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
1808 convertOptimisticReturnValue();
1809 }
1810 }.emit();
1811 }
1812 }.store();
1813 body.accept(this);
1814
1815 if(method.isReachable()) {
1816 method._goto(continueLabel);
1817 }
1818 method.label(breakLabel);
1819 }
1820
1821 /**
1822 * Initialize the slots in a frame to undefined.
1823 *
1824 * @param block block with local vars.
1825 */
1826 private void initLocals(final Block block) {
1827 lc.onEnterBlock(block);
1828
1829 final boolean isFunctionBody = lc.isFunctionBody();
1830 final FunctionNode function = lc.getCurrentFunction();
1831 if (isFunctionBody) {
1832 initializeMethodParameters(function);
1833 if(!function.isVarArg()) {
1834 expandParameterSlots(function);
1906 }
1907 }
1908
1909 tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
1910 //this symbol will be put fielded, we can't initialize it as undefined with a known type
1911 @Override
1912 public Class<?> getValueType() {
1913 if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) {
1914 return Object.class;
1915 }
1916 return paramType.getTypeClass();
1917 }
1918 });
1919 }
1920 }
1921
1922 /*
1923 * Create a new object based on the symbols and values, generate
1924 * bootstrap code for object
1925 */
1926 new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
1927 @Override
1928 protected void loadValue(final Symbol value, final Type type) {
1929 method.load(value, type);
1930 }
1931 }.makeObject(method);
1932 // program function: merge scope into global
1933 if (isFunctionBody && function.isProgram()) {
1934 method.invoke(ScriptRuntime.MERGE_SCOPE);
1935 }
1936
1937 method.storeCompilerConstant(SCOPE);
1938 if(!isFunctionBody) {
1939 // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
1940 // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
1941 // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
1942 final Label scopeEntryLabel = new Label("scope_entry");
1943 scopeEntryLabels.push(scopeEntryLabel);
1944 method.label(scopeEntryLabel);
1945 }
1946 } else if (isFunctionBody && function.isVarArg()) {
1947 // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
1948 // we need to assign them separately here.
1949 int nextParam = 0;
1950 for (final IdentNode param : function.getParameters()) {
1951 param.getSymbol().setFieldIndex(nextParam++);
4463 private void epilogue() {
4464 /**
4465 * Take the original target args from the stack and use them
4466 * together with the value to be stored to emit the store code
4467 *
4468 * The case that targetSymbol is in scope (!hasSlot) and we actually
4469 * need to do a conversion on non-equivalent types exists, but is
4470 * very rare. See for example test/script/basic/access-specializer.js
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
|
240 private final DebugLogger log;
241
242 /** From what size should we use spill instead of fields for JavaScript objects? */
243 static final int OBJECT_SPILL_THRESHOLD = Options.getIntProperty("nashorn.spill.threshold", 256);
244
245 private final Set<String> emittedMethods = new HashSet<>();
246
247 // Function Id -> ContinuationInfo. Used by compilation of rest-of function only.
248 private ContinuationInfo continuationInfo;
249
250 private final Deque<Label> scopeEntryLabels = new ArrayDeque<>();
251
252 private static final Label METHOD_BOUNDARY = new Label("");
253 private final Deque<Label> catchLabels = new ArrayDeque<>();
254 // Number of live locals on entry to (and thus also break from) labeled blocks.
255 private final IntDeque labeledBlockBreakLiveLocals = new IntDeque();
256
257 //is this a rest of compilation
258 private final int[] continuationEntryPoints;
259
260 // Scope object creators needed for for-of and for-in loops
261 private Deque<FieldObjectCreator<?>> scopeObjectCreators = new ArrayDeque<>();
262
263 /**
264 * Constructor.
265 *
266 * @param compiler
267 */
268 CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
269 super(new CodeGeneratorLexicalContext());
270 this.compiler = compiler;
271 this.evalCode = compiler.getSource().isEvalCode();
272 this.continuationEntryPoints = continuationEntryPoints;
273 this.callSiteFlags = compiler.getScriptEnvironment()._callsite_flags;
274 this.log = initLogger(compiler.getContext());
275 }
276
277 @Override
278 public DebugLogger getLogger() {
279 return log;
280 }
281
282 @Override
1264 method.breakLabel(entryLabel, lc.getUsedSlotCount());
1265 } else {
1266 method.label(entryLabel);
1267 }
1268 if(!method.isReachable()) {
1269 return false;
1270 }
1271 if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
1272 return false;
1273 }
1274 initLocals(block);
1275
1276 assert lc.getUsedSlotCount() == method.getFirstTemp();
1277 return true;
1278 }
1279
1280 boolean useOptimisticTypes() {
1281 return !lc.inSplitNode() && compiler.useOptimisticTypes();
1282 }
1283
1284 // Determine whether a block needs to provide its scope object creator for use by its child nodes.
1285 // This is only necessary for synthetic parent blocks of for-in loops with lexical declarations.
1286 boolean providesScopeCreator(final Block block) {
1287 return block.needsScope() && compiler.getScriptEnvironment()._es6
1288 && block.isSynthetic() && (block.getLastStatement() instanceof ForNode)
1289 && ((ForNode) block.getLastStatement()).needsScopeCreator();
1290 }
1291
1292 @Override
1293 public Node leaveBlock(final Block block) {
1294 popBlockScope(block);
1295 method.beforeJoinPoint(block);
1296
1297 closeBlockVariables(block);
1298 lc.releaseSlots();
1299 assert !method.isReachable() || (lc.isFunctionBody() ? 0 : lc.getUsedSlotCount()) == method.getFirstTemp() :
1300 "reachable="+method.isReachable() +
1301 " isFunctionBody=" + lc.isFunctionBody() +
1302 " usedSlotCount=" + lc.getUsedSlotCount() +
1303 " firstTemp=" + method.getFirstTemp();
1304
1305 return block;
1306 }
1307
1308 private void popBlockScope(final Block block) {
1309 final Label breakLabel = block.getBreakLabel();
1310
1311 if (providesScopeCreator(block)) {
1312 scopeObjectCreators.pop();
1313 }
1314 if(!block.needsScope() || lc.isFunctionBody()) {
1315 emitBlockBreakLabel(breakLabel);
1316 return;
1317 }
1318
1319 final Label beginTryLabel = scopeEntryLabels.pop();
1320 final Label recoveryLabel = new Label("block_popscope_catch");
1321 emitBlockBreakLabel(breakLabel);
1322 final boolean bodyCanThrow = breakLabel.isAfter(beginTryLabel);
1323 if(bodyCanThrow) {
1324 method._try(beginTryLabel, breakLabel, recoveryLabel);
1325 }
1326
1327 Label afterCatchLabel = null;
1328
1329 if(method.isReachable()) {
1330 popScope();
1331 if(bodyCanThrow) {
1332 afterCatchLabel = new Label("block_after_catch");
1333 method._goto(afterCatchLabel);
1809 }
1810
1811 @Override
1812 protected void evaluate() {
1813 new OptimisticOperation((Optimistic)forNode.getInit(), TypeBounds.UNBOUNDED) {
1814 @Override
1815 void loadStack() {
1816 method.load(ITERATOR_TYPE, iterSlot);
1817 }
1818
1819 @Override
1820 void consumeStack() {
1821 method.invoke(interfaceCallNoLookup(ITERATOR_CLASS, "next", Object.class));
1822 convertOptimisticReturnValue();
1823 }
1824 }.emit();
1825 }
1826 }.store();
1827 body.accept(this);
1828
1829 if (forNode.needsScopeCreator() && providesScopeCreator(lc.getCurrentBlock())) {
1830 // for-in loops with lexical declaration need a new scope for each iteration.
1831 final FieldObjectCreator<?> creator = scopeObjectCreators.peek();
1832 assert creator != null;
1833 creator.createForInIterationScope(method);
1834 method.storeCompilerConstant(SCOPE);
1835 }
1836
1837 if(method.isReachable()) {
1838 method._goto(continueLabel);
1839 }
1840 method.label(breakLabel);
1841 }
1842
1843 /**
1844 * Initialize the slots in a frame to undefined.
1845 *
1846 * @param block block with local vars.
1847 */
1848 private void initLocals(final Block block) {
1849 lc.onEnterBlock(block);
1850
1851 final boolean isFunctionBody = lc.isFunctionBody();
1852 final FunctionNode function = lc.getCurrentFunction();
1853 if (isFunctionBody) {
1854 initializeMethodParameters(function);
1855 if(!function.isVarArg()) {
1856 expandParameterSlots(function);
1928 }
1929 }
1930
1931 tuples.add(new MapTuple<Symbol>(symbol.getName(), symbol, paramType, paramSymbol) {
1932 //this symbol will be put fielded, we can't initialize it as undefined with a known type
1933 @Override
1934 public Class<?> getValueType() {
1935 if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) {
1936 return Object.class;
1937 }
1938 return paramType.getTypeClass();
1939 }
1940 });
1941 }
1942 }
1943
1944 /*
1945 * Create a new object based on the symbols and values, generate
1946 * bootstrap code for object
1947 */
1948 final FieldObjectCreator<Symbol> creator = new FieldObjectCreator<Symbol>(this, tuples, true, hasArguments) {
1949 @Override
1950 protected void loadValue(final Symbol value, final Type type) {
1951 method.load(value, type);
1952 }
1953 };
1954 creator.makeObject(method);
1955 if (providesScopeCreator(block)) {
1956 scopeObjectCreators.push(creator);
1957 }
1958 // program function: merge scope into global
1959 if (isFunctionBody && function.isProgram()) {
1960 method.invoke(ScriptRuntime.MERGE_SCOPE);
1961 }
1962
1963 method.storeCompilerConstant(SCOPE);
1964 if(!isFunctionBody) {
1965 // Function body doesn't need a try/catch to restore scope, as it'd be a dead store anyway. Allowing it
1966 // actually causes issues with UnwarrantedOptimismException handlers as ASM will sort this handler to
1967 // the top of the exception handler table, so it'll be triggered instead of the UOE handlers.
1968 final Label scopeEntryLabel = new Label("scope_entry");
1969 scopeEntryLabels.push(scopeEntryLabel);
1970 method.label(scopeEntryLabel);
1971 }
1972 } else if (isFunctionBody && function.isVarArg()) {
1973 // Since we don't have a scope, parameters didn't get assigned array indices by the FieldObjectCreator, so
1974 // we need to assign them separately here.
1975 int nextParam = 0;
1976 for (final IdentNode param : function.getParameters()) {
1977 param.getSymbol().setFieldIndex(nextParam++);
4489 private void epilogue() {
4490 /**
4491 * Take the original target args from the stack and use them
4492 * together with the value to be stored to emit the store code
4493 *
4494 * The case that targetSymbol is in scope (!hasSlot) and we actually
4495 * need to do a conversion on non-equivalent types exists, but is
4496 * very rare. See for example test/script/basic/access-specializer.js
4497 */
4498 target.accept(new SimpleNodeVisitor() {
4499 @Override
4500 protected boolean enterDefault(final Node node) {
4501 throw new AssertionError("Unexpected node " + node + " in store epilogue");
4502 }
4503
4504 @Override
4505 public boolean enterIdentNode(final IdentNode node) {
4506 final Symbol symbol = node.getSymbol();
4507 assert symbol != null;
4508 if (symbol.isScope()) {
4509 final int flags = getScopeCallSiteFlags(symbol) | (node.isDeclaredHere() ? CALLSITE_DECLARE : 0);
4510 if (isFastScope(symbol)) {
4511 storeFastScopeVar(symbol, flags);
4512 } else {
4513 method.dynamicSet(node.getName(), flags, false);
4514 }
4515 } else {
4516 final Type storeType = assignNode.getType();
4517 assert storeType != Type.LONG;
4518 if (symbol.hasSlotFor(storeType)) {
4519 // Only emit a convert for a store known to be live; converts for dead stores can
4520 // give us an unnecessary ClassCastException.
4521 method.convert(storeType);
4522 }
4523 storeIdentWithCatchConversion(node, storeType);
4524 }
4525 return false;
4526
4527 }
4528
4529 @Override
|