| /* |
| * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code 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 |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package com.apple.internal.jobjc.generator.utils; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import com.apple.internal.jobjc.generator.Utils; |
| import com.apple.internal.jobjc.generator.model.Framework; |
| import com.apple.internal.jobjc.generator.model.Struct; |
| import com.apple.internal.jobjc.generator.model.types.NType; |
| import com.apple.internal.jobjc.generator.model.types.TypeCache; |
| import com.apple.internal.jobjc.generator.model.types.NType.NField; |
| import com.apple.internal.jobjc.generator.model.types.NType.NStruct; |
| import com.apple.internal.jobjc.generator.utils.Fp.Map1; |
| import com.apple.jobjc.JObjCRuntime.Width; |
| import java.util.Date; |
| |
| /** |
| * Takes a framework, compiles a native source file with all its structs, |
| * and figures out their sizes and field offsets. |
| */ |
| public class StructOffsetResolver { |
| public void resolve(Collection<Framework> fws){ |
| try { |
| _resolve(fws); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| protected void _resolve(final Collection<Framework> fws) throws Exception{ |
| for(final Framework fw : fws){ |
| for(final Width width : Width.values()){ |
| System.out.println("SOR -- Getting Struct offsets @" + width + " for " + fw.name); |
| String nativeSrc = generateFileForFramework(fw, width); |
| String executable = compileObjC(nativeSrc, width); |
| execute(executable, new Map1<String,Object>(){ |
| public Object apply(String ln) { |
| try { |
| processLine(ln, fws, width); |
| return null; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| static Set<String> alwaysHeaders_shared = new TreeSet<String>(Arrays.asList( |
| "<Cocoa/Cocoa.h>", |
| "\"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/HIToolbox.h\"")); |
| static Map<Width,Set<String>> alwaysHeaders = Fp.litMap( |
| Width.W32, alwaysHeaders_shared, |
| Width.W64, alwaysHeaders_shared); |
| |
| static Set<String> bannedHeaders_shared = new TreeSet<String>(Arrays.asList( |
| "NSJavaSetup.h", "IMKInputController.h", "NSSimpleHorizontalTypesetter.h", "NSSpellServer.h", "IMKServer.h", "IKImageBrowserCell.h")); |
| static Map<Width,Set<String>> bannedHeaders = Fp.litMap( |
| Width.W32, bannedHeaders_shared, |
| Width.W64, Fp.appendSet(bannedHeaders_shared, |
| Arrays.asList("npapi.h", "npruntime.h", "npfunctions.h"))); |
| |
| // We can cache the last accessed framework because, 99% of the time, |
| // the caller will ask for the same one, over and over again. |
| protected Framework cachedFw; |
| protected Framework findFrameworkByName(Collection<Framework> fws, String name){ |
| if(cachedFw != null && cachedFw.name.equals(name)) |
| return cachedFw; |
| cachedFw = null; |
| for(Framework fw : fws) |
| if(fw.name.equals(name)){ |
| cachedFw = fw; |
| break; |
| } |
| return cachedFw; |
| } |
| |
| protected void processLine(String ln, Collection<Framework> fws, Width arch) throws Exception{ |
| System.out.println("\tSOR '" + ln + "'"); |
| if(ln.trim().length() == 0) return; |
| Pattern stinfo = Pattern.compile("^(.*) (.*):(\\d+).*$"); |
| Matcher m = stinfo.matcher(ln); |
| if(!m.matches()) throw new RuntimeException("Failed to parse line from exec: " + ln); |
| String fwname = m.group(1); |
| String stname = m.group(2); |
| int stsize = Integer.parseInt(m.group(3)); |
| |
| Framework fw = findFrameworkByName(fws, fwname); |
| |
| Struct st = fw.getStructByName(stname); |
| NStruct nst = wget(arch, st.type.type32, st.type.type64); |
| nst.sizeof.put(arch, stsize); |
| |
| // System.out.println(st.name + " : " + stsize); |
| |
| Pattern finfo = Pattern.compile(" (-?\\d+)"); |
| Matcher fm = finfo.matcher(ln); |
| int fi = 0; |
| while(fm.find()){ |
| NField sf = nst.fields.get(fi++); |
| sf.offset.put(arch, Integer.parseInt(fm.group(1))); |
| // System.out.println("\t" + sf.name + " : " + off); |
| } |
| |
| TypeCache.inst().pingType(st.type); |
| } |
| |
| /** |
| * Generates Objective-C file and returns absolute path name. |
| */ |
| private String generateFileForFramework(Framework fw, Width arch) throws Exception{ |
| File tempfile = File.createTempFile("JObjC-SOR-" + fw.name + "-" + arch + "-", ".mm"); |
| PrintWriter out = new PrintWriter(new FileWriter(tempfile)); |
| out.println("#include<iostream>"); |
| printHeaderLines(fw, arch, out); |
| out.println(""); |
| out.println("int main(int argc, char** argv){"); |
| printStructInfos(fw, arch, out); |
| out.println("\treturn 0;"); |
| out.println("}"); |
| out.close(); |
| return tempfile.getAbsolutePath(); |
| } |
| |
| protected void execute(String executable, Fp.Map1<String,Object> lineProcessor) throws Exception { |
| // System.out.println(">>>> Executing " + new Date().toString()); |
| Process p = Runtime.getRuntime().exec(new String[]{executable}); |
| |
| if(lineProcessor != null){ |
| BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); |
| String line; |
| while ((line = stdout.readLine()) != null) |
| lineProcessor.apply(line); |
| stdout.close(); |
| } |
| p.waitFor(); |
| if(p.exitValue() != 0) |
| throw new RuntimeException(executable + " did not execute successfully: " + p.exitValue()); |
| } |
| |
| private static Map<Width,String> gccFlag = Fp.litMap(Width.W32, "-m32", Width.W64, "-m64"); |
| |
| static boolean isDone(Process p){ |
| try{ |
| p.exitValue(); |
| return true; |
| } |
| catch(Exception x){ |
| return false; |
| } |
| } |
| |
| protected static String compileObjC(String nativeSrc, Width arch) throws Exception { |
| String execPath = nativeSrc.replace(".mm", ""); |
| Process p = Runtime.getRuntime().exec(new String[]{ |
| "llvm-g++", "-Wall", gccFlag.get(arch), "-ObjC++", "-framework", "Foundation", "-o", execPath, nativeSrc |
| }); |
| BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream())); |
| BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream())); |
| while(!isDone(p)){ |
| while(stdout.ready()) System.out.println(stdout.readLine()); |
| while(stderr.ready()) System.out.println(stderr.readLine()); |
| } |
| p.waitFor(); |
| while(stdout.ready() || stderr.ready()){ |
| if(stdout.ready()) System.out.println(stdout.readLine()); |
| if(stderr.ready()) System.out.println(stderr.readLine()); |
| } |
| if(p.exitValue() != 0) |
| throw new RuntimeException("gcc did not compile '" + nativeSrc + "' successfully: " + p.exitValue()); |
| return execPath; |
| } |
| |
| static void printStructInfos(Framework fw, Width arch, PrintWriter out){ |
| for(Struct st : fw.structs){ |
| NStruct nst = wget(arch, st.type.type32, st.type.type64); |
| out.println("std::cout << \"" + fw.name + " " + st.name + "\" << ':' << sizeof("+st.name+")"); |
| for(NField sf : nst.fields){ |
| out.print("\t<< ' ' << "); |
| out.println(sf.type instanceof NType.NBitfield |
| ? "-1" |
| : "offsetof("+st.name+","+sf.name+")"); |
| } |
| out.println("\t<< std::endl;"); |
| } |
| } |
| |
| static void printHeaderLines(Framework fw, Width arch, PrintWriter out) throws Exception { |
| Collection<String> always = alwaysHeaders.get(arch); |
| Collection<String> banned = bannedHeaders.get(arch); |
| out.println("#define COREFOUNDATION_CFPLUGINCOM_SEPARATE 0"); |
| for(String header : always) |
| out.println("#import " + header); |
| |
| out.println("#undef COREFOUNDATION_CFPLUGINCOM_SEPARATE"); |
| String umbrella = fw.path + "/Headers/" + fw.name; |
| if(new File(umbrella).exists()) |
| out.println("#import \"" + umbrella + "\""); |
| |
| for(File header : getHeaders(fw)) |
| if(!banned.contains(header.getName())) |
| out.println("#import \"" + header.getAbsolutePath() + "\""); |
| } |
| |
| static <A,B> A wget(Width arch, B x32, B x64){ |
| switch(arch){ |
| case W32: return (A) x32; |
| case W64: return (A) x64; |
| default: throw new RuntimeException(); |
| } |
| } |
| |
| /** |
| * Gets the absolute path to every header in FOO.framework/Headers |
| */ |
| static Collection<File> getHeaders(Framework fw) throws Exception { |
| String hpath = fw.path + "/Headers"; |
| return Utils.find(new File(hpath), "^.*\\.h$", ""); |
| } |
| } |