Skip to content
Permalink
Browse files
Handling static lambda calls
  • Loading branch information
Jaroslav Tulach committed Jul 15, 2021
1 parent 929caa3 commit 8cb5bcad7c8529b88cd40df080550c97e0901e7a
Showing 6 changed files with 143 additions and 15 deletions.
@@ -1626,9 +1626,9 @@ public String[] getAccess() {
} // end InnerClassData

static class BootMethodData {
private final ClassData clazz;
final ClassData clazz;
final int method;
private final int[] args;
final int[] args;

private BootMethodData(ClassData clazz, int method, int[] args) {
this.clazz = clazz;
@@ -258,13 +258,22 @@ private String compileImpl(Appendable out, final String cn) throws IOException {
out.append("\n function ").append(className).append("fillInstOf(x) {");
String instOfName = "$instOf_" + className;
out.append("\n Object.defineProperty(x, '").append(instOfName).append("', { value : true });");
MethodData functionalInterfaceMethod = null;
if (jc.isInterface()) {
int cnt = 0;
for (MethodData m : jc.getMethods()) {
if ((m.getAccess() & ACC_ABSTRACT) == 0
&& (m.getAccess() & ACC_STATIC) == 0
&& (m.getAccess() & ACC_PRIVATE) == 0) {
final String mn = findMethodName(m, new StringBuilder());
out.append("\n if (!x['").append(mn).append("']) Object.defineProperty(x, '").append(mn).append("', { value : c['").append(mn).append("']});");
if ((m.getAccess() & ACC_ABSTRACT) == 0) {
if ((m.getAccess() & ACC_STATIC) == 0
&& (m.getAccess() & ACC_PRIVATE) == 0) {
final String mn = findMethodName(m, new StringBuilder());
out.append("\n if (!x['").append(mn).append("']) Object.defineProperty(x, '").append(mn).append("', { value : c['").append(mn).append("']});");
}
} else {
functionalInterfaceMethod = m;
cnt++;
}
if (cnt != 1) {
functionalInterfaceMethod = null;
}
}
}
@@ -282,6 +291,14 @@ private String compileImpl(Appendable out, final String cn) throws IOException {
out.append(accessClass("java_lang_Class")).append("(true);");
out.append("\n CLS.$class.jvmName = '").append(cn).append("';");
out.append("\n CLS.$class.superclass = sprcls;");
if (functionalInterfaceMethod != null) {
final String mn = findMethodName(functionalInterfaceMethod, new StringBuilder());
out.append("\n CLS.$class.functional = function(arr, fn) {");
out.append("\n var inst = new CLS();");
out.append("\n inst['").append(mn).append("'] = function() { return fn(arr, arguments); };");
out.append("\n return inst;");
out.append("\n };");
}
out.append("\n CLS.$class.interfaces = function() { return [");
{
boolean first = true;
@@ -542,7 +559,7 @@ static int readShortArg(byte[] byteCodes, int offsetInstruction) {
return readShort(byteCodes, offsetInstruction + 1);
}

private static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
static void countArgs(String descriptor, char[] returnType, StringBuilder sig, StringBuilder cnt) {
int i = 0;
Boolean count = null;
boolean array = false;
@@ -17,6 +17,8 @@
*/
package org.apidesign.vm4brwsr;

import java.io.IOException;

abstract class IndyHandler {
final String factoryClazz;
final String factoryMethod;
@@ -26,13 +28,19 @@ abstract class IndyHandler {
this.factoryMethod = method;
}

protected abstract boolean handle(Ctx ctx);
protected abstract boolean handle(Ctx ctx) throws IOException ;

static class Ctx {
final Appendable out;
final StackMapper stackMapper;
final ByteCodeToJavaScript byteCodeToJavaScript;
final ByteCodeParser.BootMethodData bm;
final String[] mt;

Ctx(String[] methodAndType, ByteCodeParser.BootMethodData bm) {
Ctx(Appendable out, StackMapper m, ByteCodeToJavaScript bc, String[] methodAndType, ByteCodeParser.BootMethodData bm) {
this.out = out;
this.stackMapper = m;
this.byteCodeToJavaScript = bc;
this.mt = methodAndType;
this.bm = bm;
}
@@ -1014,7 +1014,7 @@ void loopCode(
private void handleIndy(int indx, String[] methodAndType, String[] mi, ByteCodeParser.BootMethodData bm, StackMapper mapper) throws IOException {
for (IndyHandler h : byteCodeToJavaScript.getIndyHandlers()) {
if (h.factoryClazz.equals(mi[0]) && h.factoryMethod.equals(mi[1])) {
IndyHandler.Ctx ctx = new IndyHandler.Ctx(methodAndType, bm);
IndyHandler.Ctx ctx = new IndyHandler.Ctx(out, mapper, byteCodeToJavaScript, methodAndType, bm);
if (h.handle(ctx)) {
return;
}
@@ -17,17 +17,82 @@
*/
package org.apidesign.vm4brwsr;

import java.io.IOException;

final class MetafactoryHandler extends IndyHandler {
MetafactoryHandler() {
super("java/lang/invoke/LambdaMetafactory", "metafactory");
}

@Override
protected boolean handle(Ctx ctx) {
for (int i = 0; i < ctx.bm.getArguments(); i++) {
System.err.println(" arg#" + i + " = " + ctx.bm.getArgument(i));
protected boolean handle(Ctx ctx) throws IOException {
final int fixedArgsCount;
{
final String sig = ctx.mt[1];
int typeEnd = sig.lastIndexOf(')');
String typeSig = sig.substring(typeEnd + 1);
if (!typeSig.startsWith("L") || !typeSig.endsWith(";")) {
return false;
}
final String type = typeSig.substring(1, typeSig.length() - 1);
ctx.byteCodeToJavaScript.requireReference(type);
final String mangledType = ByteCodeToJavaScript.mangleClassName(type);
String interfaceToCreate = ctx.byteCodeToJavaScript.accessClassFalse(mangledType);

StringBuilder sigB = new StringBuilder();
StringBuilder cnt = new StringBuilder();
char[] returnType = { 'V' };
boolean isStatic = true;
ByteCodeToJavaScript.countArgs(sig, returnType, sigB, cnt);

fixedArgsCount = isStatic ? cnt.length() : cnt.length() + 1;
final CharSequence[] vars = new CharSequence[fixedArgsCount];
for (int j = fixedArgsCount - 1; j >= 0; --j) {
vars[j] = ctx.stackMapper.popValue();
}

assert returnType[0] == 'L';

ctx.stackMapper.flush(ctx.out);

final Variable samVar = ctx.stackMapper.pushA();
ctx.out.append("var ").append(samVar).append(" = ").append(interfaceToCreate).append(".constructor.$class.functional([");

String sep = "";
for (int j = 0; j < fixedArgsCount; j++) {
ctx.out.append(sep).append(vars[j]);
sep = ", ";
}

ctx.out.append("], function(args1, args2) {\n");
}
{
ByteCodeParser.CPX2 methodHandle = ctx.bm.clazz.getCpoolEntry(ctx.bm.args[1]);

String[] methodInfoName = ctx.bm.clazz.getFieldInfoName(methodHandle.cpx2);
ctx.byteCodeToJavaScript.requireReference(methodInfoName[0]);
final String mangledType = ByteCodeToJavaScript.mangleClassName(methodInfoName[0]);
StringBuilder cnt = new StringBuilder();
char[] returnType = { 'V' };
String mangledMethod = ByteCodeToJavaScript.findMethodName(methodInfoName, cnt, returnType);

ctx.out.append("\n return ");
ctx.out.append(ctx.byteCodeToJavaScript.accessClassFalse(mangledType));
ctx.out.append(".").append(mangledMethod).append('(');
String sep = "";
for (int i = 0; i < cnt.length(); i++) {
ctx.out.append(sep);
if (i < fixedArgsCount) {
ctx.out.append("args1[" + i + "]");
} else {
ctx.out.append("args2[" + (i - fixedArgsCount) + "]");
}
sep = ", ";
}
ctx.out.append(");");
ctx.out.append("\n })");
}
return false;
return true;
}

}
@@ -0,0 +1,38 @@
/**
* Back 2 Browser Bytecode Translator
* Copyright (C) 2012-2018 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. Look for COPYING file in the top folder.
* If not, see http://opensource.org/licenses/GPL-2.0.
*/
package org.apidesign.bck2brwsr.vm8;

import org.apidesign.bck2brwsr.vmtest.Compare;
import org.apidesign.bck2brwsr.vmtest.VMTest;
import org.testng.annotations.Factory;

public class SamTest {

@Compare
public int staticLambdas() {
int[] sum = {0};
Functions.Compute<Integer> inc = () -> sum[0]++;
Functions.Compute<Integer> ret = () -> sum[0];
inc.get();
return ret.get();
}

@Factory public static Object[] create() {
return VMTest.create(SamTest.class);
}
}

0 comments on commit 8cb5bca

Please sign in to comment.