boolean isProperFunctor(ClassReader type) { if (!type.hasModifier(ElementModifier.INTERFACE)) { return false; } return type.getMethods().stream() .filter(method -> method.hasModifier(ElementModifier.ABSTRACT)) .count() == 1; }
private static boolean needsClinitCall(MethodReader method) { if (method.getName().equals("<clinit>")) { return false; } if (method.getName().equals("<init>")) { return true; } return method.hasModifier(ElementModifier.STATIC); } }
private boolean isWrappedNativeCall(MethodReader method) { if (!method.hasModifier(ElementModifier.NATIVE)) { return false; } for (ValueType type : method.getParameterTypes()) { if (type.isObject(String.class)) { return true; } } return false; }
private void collectMethodsToCopyFromInterfacesImpl(ClassReader cls, List<MethodReference> targetList, Set<MethodDescriptor> visited, Set<String> visitedClasses) { if (!visitedClasses.add(cls.getName())) { return; } for (MethodReader method : cls.getMethods()) { if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.ABSTRACT)) { if (visited.add(method.getDescriptor())) { targetList.add(method.getReference()); } } } for (String ifaceName : cls.getInterfaces()) { ClassReader iface = classSource.get(ifaceName); if (iface != null) { collectMethodsToCopyFromInterfacesImpl(iface, targetList, visited, visitedClasses); } } }
private MethodReader extractSingleMethod(Diagnostics diagnostics, CallLocation location, ClassReader cls) { MethodReader candidate = null; for (MethodReader method : cls.getMethods()) { if (method.hasModifier(ElementModifier.STATIC) || !method.hasModifier(ElementModifier.ABSTRACT)) { continue; } if (candidate != null) { diagnostics.error(location, "Function class {{c0}} must have one abstract method, it has multiple", cls.getName()); return null; } else { candidate = method; } } if (candidate == null) { diagnostics.error(location, "Function class {{c0}} must have one abstract method, it has none", cls.getName()); return null; } return candidate; }
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (classReader == null || (access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) { return null; } JCLItem item = new JCLItem(JCLItemType.METHOD, name + desc); MethodReader method = findMethod(classReader, MethodDescriptor.parse(name + desc)); if (method == null) { item.status = JCLStatus.MISSING; } else { if ((access & Opcodes.ACC_ABSTRACT) == 0 && method.hasModifier(ElementModifier.ABSTRACT)) { item.status = JCLStatus.MISSING; } else { item.status = method.getOwnerName().equals(classReader.getName()) ? JCLStatus.FOUND : JCLStatus.PARTIAL; } } item.visibility = (access & Opcodes.ACC_PROTECTED) != 0 ? JCLVisibility.PROTECTED : JCLVisibility.PUBLIC; jclClass.items.add(item); if (item.status == JCLStatus.MISSING) { jclClass.status = JCLStatus.PARTIAL; } return null; }
private void findOverriddenMethods(String className, MethodDescriptor methodDesc, Set<MethodReference> result, Set<String> visited) { if (!visited.add(className)) { return; } if (methodDesc.getName().equals("<init>") || methodDesc.getName().equals("<clinit>")) { return; } ClassReader cls = classSource.get(className); if (cls == null) { return; } MethodReader method = cls.getMethod(methodDesc); if (method != null) { if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL)) { result.add(method.getReference()); } } else { if (cls.getParent() != null) { findOverriddenMethods(cls.getParent(), methodDesc, result, visited); } for (String iface : cls.getInterfaces()) { findOverriddenMethods(iface, methodDesc, result, visited); } } }
private MethodReference createCallbackMethod(MethodReader method) { int paramCount = method.parameterCount(); if (!method.hasModifier(ElementModifier.STATIC)) { paramCount++; } ValueType[] signature = new ValueType[paramCount + 1]; for (int i = 0; i < paramCount; ++i) { signature[i] = ValueType.object(JSObject.class.getName()); } signature[paramCount] = method.getResultType() == ValueType.VOID ? ValueType.VOID : ValueType.object(JSObject.class.getName()); return new MethodReference(location.getMethod().getClassName(), method.getName() + "$jsocb$_" + idGenerator++, signature); }
if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) { return method;
private boolean hasMonitor(MethodReader method) { if (method.hasModifier(ElementModifier.SYNCHRONIZED)) { return true; } ProgramReader program = method.getProgram(); AsyncInstructionReader insnReader = new AsyncInstructionReader(); for (int i = 0; i < program.basicBlockCount(); ++i) { program.basicBlockAt(i).readAllInstructions(insnReader); if (insnReader.async) { return true; } } return false; }
private void reachFunctorMethods(DependencyAgent agent, String type) { ClassReader cls = agent.getClassSource().get(type); if (cls != null) { for (MethodReader method : cls.getMethods()) { if (!method.hasModifier(ElementModifier.STATIC)) { agent.linkMethod(method.getReference()).use(); } } } }
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) { if (!isProperFunctor(type)) { diagnostics.error(location, "Wrong functor: {{c0}}", type.getName()); return var; } String name = type.getMethods().stream() .filter(method -> method.hasModifier(ElementModifier.ABSTRACT)) .findFirst().get().getName(); Variable functor = program.createVariable(); Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation()); InvokeInstruction insn = new InvokeInstruction(); insn.setType(InvocationType.SPECIAL); insn.setMethod(JSMethods.FUNCTION); insn.setReceiver(functor); insn.setArguments(var, nameVar); insn.setLocation(location.getSourceLocation()); replacement.add(insn); return functor; }
Variable unwrapFunctor(CallLocation location, Variable var, ClassReader type) { if (!isProperFunctor(type)) { diagnostics.error(location, "Wrong functor: {{c0}}", type.getName()); return var; } String name = type.getMethods().stream() .filter(method -> method.hasModifier(ElementModifier.ABSTRACT)) .findFirst().get().getName(); Variable functor = program.createVariable(); Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation()); InvokeInstruction insn = new InvokeInstruction(); insn.setType(InvocationType.SPECIAL); insn.setMethod(JSMethods.FUNCTION_AS_OBJECT); insn.setReceiver(functor); insn.setArguments(var, nameVar); insn.setLocation(location.getSourceLocation()); replacement.add(insn); return functor; }
private void findInheritedMethods(ClassReader cls, Set<MethodDescriptor> methods, Set<String> visited) { if (!visited.add(cls.getName())) { return; } if (typeHelper.isJavaScriptClass(cls.getName())) { for (MethodReader method : cls.getMethods()) { if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.FINAL) && method.getLevel() != AccessLevel.PRIVATE) { methods.add(method.getDescriptor()); } } } else if (typeHelper.isJavaScriptImplementation(cls.getName())) { if (cls.getParent() != null) { ClassReader parentCls = classSource.get(cls.getParent()); if (parentCls != null) { findInheritedMethods(parentCls, methods, visited); } } for (String iface : cls.getInterfaces()) { ClassReader parentCls = classSource.get(iface); if (parentCls != null) { findInheritedMethods(parentCls, methods, visited); } } } }
if (!method.hasModifier(ElementModifier.STATIC)) { ++requiredParams;
@Override public void methodReached(DependencyAgent agent, MethodDependency method) { if (!reachedMethods.add(method.getReference())) { return; } ValueType type = method.getMethod().getResultType(); while (type instanceof ValueType.Array) { type = ((ValueType.Array) type).getItemType(); } if (type instanceof ValueType.Object) { String className = ((ValueType.Object) type).getClassName(); ClassReader cls = agent.getClassSource().get(className); if (cls != null && cls.hasModifier(ElementModifier.ANNOTATION)) { agent.linkClass(className); } } if (method.getMethod().hasModifier(ElementModifier.STATIC) && method.getMethod().getName().equals("$$__readAnnotations__$$")) { ClassReader cls = agent.getClassSource().get(method.getReference().getClassName()); if (cls != null) { for (AnnotationReader annotation : cls.getAnnotations().all()) { agent.linkClass(annotation.getType()); } } } MethodReference methodRef = method.getMethod().getReference(); if (methodRef.getClassName().equals("java.lang.Class") && methodRef.getName().equals("getAnnotations")) { reachGetAnnotations(agent, method.getVariable(0)); } }
@Override protected void processMethod(MethodDependency methodDep) { MethodReader method = methodDep.getMethod(); ProgramReader program = method.getProgram(); if (program != null) { FastInstructionAnalyzer instructionAnalyzer = new FastInstructionAnalyzer(this); instructionAnalyzer.setCaller(method.getReference()); for (BasicBlockReader block : program.getBasicBlocks()) { block.readAllInstructions(instructionAnalyzer); for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) { if (tryCatch.getExceptionType() != null) { linkClass(tryCatch.getExceptionType()); } } } methodDep.variableNodes = new DependencyNode[program.variableCount()]; for (int i = 0; i < methodDep.variableNodes.length; ++i) { methodDep.variableNodes[i] = instancesNode; } } if (method.hasModifier(ElementModifier.SYNCHRONIZED)) { processAsyncMethod(methodDep); } }
private void validateSignature(MethodReader method) { if (!method.hasModifier(ElementModifier.STATIC)) { if (!typeHelper.isJavaScriptClass(method.getOwnerName())) { diagnostics.error(location, "Can't call method {{m0}} of non-JS class", method.getReference()); } } for (int i = 0; i < method.parameterCount(); ++i) { if (!typeHelper.isSupportedType(method.parameterType(i))) { diagnostics.error(location, "Invalid type {{t0}} of parameter " + (i + 1) + " of method {{m1}}", method.parameterType(i), method.getReference()); } } if (method.getResultType() != ValueType.VOID && !typeHelper.isSupportedType(method.getResultType())) { diagnostics.error(location, "Invalid type {{t0}} of return value of method {{m1}}", method.getResultType(), method.getReference()); } }
writer.outdent().append("}).call(").append(!method.hasModifier(ElementModifier.STATIC) ? context.getParameterName(0) : "null"); for (int i = 0; i < args.size(); ++i) {
private void renderCallable(SourceWriter writer, MethodReader method) throws IOException { writer.append("function(obj,").ws().append("args)").ws().append("{").indent().softNewLine(); initClass(writer, method); if (method.getResultType() != ValueType.VOID) { writer.append("return "); } writer.appendMethodBody(method.getReference()); writer.append('('); boolean first = true; if (!method.hasModifier(ElementModifier.STATIC)) { writer.append("obj").ws(); first = false; } for (int i = 0; i < method.parameterCount(); ++i) { if (!first) { writer.append(',').ws(); } first = false; int index = i; unboxIfNecessary(writer, method.parameterType(i), () -> writer.append("args[" + index + "]")); } writer.append(");").softNewLine(); if (method.getResultType() == ValueType.VOID) { writer.append("return null;").softNewLine(); } writer.outdent().append("}"); }