package com.intellij.flex.uiDesigner.debug; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.GenericProgramRunner; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.flex.uiDesigner.DebugPathManager; import com.intellij.lang.javascript.flex.debug.FlexDebugProcess; import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration; import com.intellij.lang.javascript.flex.run.BCBasedRunnerParameters; import com.intellij.lang.javascript.flex.run.RemoteFlashRunConfiguration; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.xdebugger.XDebugProcess; import com.intellij.xdebugger.XDebugProcessStarter; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerManager; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.StringTokenizer; // we need SILENTLY_DETACH_ON_CLOSE, but RunContentManagerImpl provides only ProcessHandler.SILENTLY_DESTROY_ON_CLOSE public class FlexRunner extends GenericProgramRunner { private static final String SKIP_MARKER = "^"; private final Callback callback; private FlexBuildConfiguration buildConfiguration; public FlexRunner(Callback callback, FlexBuildConfiguration buildConfiguration) { this.callback = callback; this.buildConfiguration = buildConfiguration; } @Override protected RunContentDescriptor doExecute(@NotNull final RunProfileState state, @NotNull ExecutionEnvironment env) throws ExecutionException { final BCBasedRunnerParameters parameters = ((RemoteFlashRunConfiguration)env.getRunProfile()).getRunnerParameters(); RunContentDescriptor runContentDescriptor = XDebuggerManager.getInstance(env.getProject()).startSession(env, new XDebugProcessStarter() { @Override @NotNull public XDebugProcess start(@NotNull final XDebugSession session) throws ExecutionException { try { return DebugPathManager.IS_DEV ? new MyFlexDebugProcessAbleToResolveFileDebugId(callback, session, buildConfiguration, parameters) : new MyFlexDebugProcess(callback, session, buildConfiguration, parameters); } catch (IOException e) { throw new ExecutionException(e.getMessage(), e); } finally { buildConfiguration = null; } } }).getRunContentDescriptor(); ProcessHandler processHandler = runContentDescriptor.getProcessHandler(); assert processHandler != null; //noinspection deprecation processHandler.putUserData(ProcessHandler.SILENTLY_DESTROY_ON_CLOSE, true); return runContentDescriptor; } @Override @NotNull public String getRunnerId() { return "FlexDebugRunnerForDesignView"; } @Override public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { return true; } private static class MyFlexDebugProcess extends FlexDebugProcess { private final Callback callback; public MyFlexDebugProcess(Callback callback, XDebugSession session, FlexBuildConfiguration buildConfiguration, BCBasedRunnerParameters parameters) throws IOException { super(session, buildConfiguration, parameters); this.callback = callback; } @Override public void stop() { if (DebugPathManager.IS_DEV) { super.stop(); } } @Override protected void notifyFdbWaitingForPlayerStateReached() { callback.processStarted(getSession().getRunContentDescriptor()); } } private static class MyFlexDebugProcessAbleToResolveFileDebugId extends MyFlexDebugProcess { public MyFlexDebugProcessAbleToResolveFileDebugId(Callback callback, XDebugSession session, FlexBuildConfiguration buildConfiguration, BCBasedRunnerParameters parameters) throws IOException { super(callback, session, buildConfiguration, parameters); } //@Override protected void processShowFilesResult(StringTokenizer tokenizer) { final Map<String, String> libNameToSourceRoot = new THashMap<>(); while (tokenizer.hasMoreTokens()) { final String line = tokenizer.nextToken(); final int spaceIndex = line.indexOf(' '); final int commaPos = line.indexOf(','); if (spaceIndex == -1 || commaPos == -1) { log("Unexpected string format:" + line); continue; } String id = line.substring(0, spaceIndex); String fullPath; if (line.charAt(spaceIndex + 1) == '$') { if (line.indexOf("$framework/mx/styles/StyleProtoChain.as", spaceIndex) != -1) { fullPath = DebugPathManager.getFudHome() + "/flex-injection/src/main/flex/mx/styles/StyleProtoChain.as"; } else if (line.indexOf("$framework/mx/effects/Effect.as", spaceIndex) != -1) { fullPath = DebugPathManager.getFudHome() + "/flex-injection/src/main/flex/mx/effects/Effect.as"; } else { int firstSlashIndex = line.indexOf('/'); StringBuilder builder = StringBuilderSpinAllocator.alloc(); try { boolean isBackSlash = firstSlashIndex == -1; if (isBackSlash) { firstSlashIndex = line.indexOf('\\'); } builder.append(getAppSdkHome()).append("/frameworks/projects/").append(line, spaceIndex + 2, firstSlashIndex).append("/src/"); if (isBackSlash) { builder.append(line.substring(firstSlashIndex + 1, commaPos).replace('\\', '/')); } else { builder.append(line, firstSlashIndex + 1, commaPos); } fullPath = builder.toString(); } finally { StringBuilderSpinAllocator.dispose(builder); } } } else if (line.startsWith("/Users/develar/Documents/flex", spaceIndex + 1)) { final int beginIndex = "/Users/develar/Documents/flex/frameworks/projects/".length() + spaceIndex + 1; final int endIndex = line.indexOf('/', beginIndex + 2); String libName = line.substring(beginIndex, endIndex); fullPath = "/Users/develar/.m2/repository/com/adobe/flex/framework/" + libName + "/4.5.0.20968/" + libName + "-4.5.0.20968-sources.jar!" + line.substring(endIndex + "src".length() + 1, commaPos); } else if (line.startsWith("/Users/develar/Documents/", spaceIndex + 1)) { fullPath = getFromAttachedSources(line, libNameToSourceRoot, commaPos); if (fullPath == null) { fullPath = line.substring(spaceIndex + 1, commaPos).replace(File.separatorChar, '/'); } } else if (line.indexOf("FtyleProtoChain.as", spaceIndex) != -1) { fullPath = "/Developer/SDKs/flex_4.5.1/frameworks/projects/framework/src/mx/styles/StyleProtoChain.as"; } else if (line.indexOf("Fffect.as", spaceIndex) != -1) { fullPath = "/Developer/SDKs/flex_4.5.1/frameworks/projects/framework/src/mx/effects/Effect.as"; } else { fullPath = line.substring(spaceIndex + 1, commaPos).replace(File.separatorChar, '/'); } String shortName = line.substring(commaPos + 2); //myFilePathToIdMap.put(fullPath, id); //addToMap(myFileNameToPathsMap, shortName, fullPath); } } private static String getFromAttachedSources(String line, Map<String, String> libNameToSourceRoot, int commaPos) { String srcDir = "/src/main/flex/"; int srcFirstSlashIndex = line.lastIndexOf(srcDir); if (srcFirstSlashIndex == -1) { srcDir = "/src/"; srcFirstSlashIndex = line.lastIndexOf(srcDir); if (srcFirstSlashIndex == -1) { return null; } } // must be wrapped - we check type (may be rb.swc, i. e. resource bundle) final String libName = ":" + line.substring(StringUtil.lastIndexOf(line, '/', 1, srcFirstSlashIndex - 1) + 1, srcFirstSlashIndex) + ":swc"; String fullPath = libNameToSourceRoot.get(libName); if (fullPath == null) { for (Project project : ProjectManager.getInstance().getOpenProjects()) { for (Library library : ProjectLibraryTable.getInstance(project).getLibraries()) { String libraryName = library.getName(); if (libraryName != null && libraryName.contains(libName)) { String[] urls = library.getUrls(OrderRootType.SOURCES); assert urls.length == 1; fullPath = urls[0]; break; } } } if (fullPath == null) { libNameToSourceRoot.put(libName, SKIP_MARKER); return null; } else { libNameToSourceRoot.put(libName, fullPath); } } else if (fullPath.equals(SKIP_MARKER)) { return null; } return fullPath + line.substring(srcFirstSlashIndex + srcDir.length() - 1, commaPos); } } }