Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d5d6b0100 | |||
| 344270a0ba | |||
| 9957f694b9 | |||
| 4b9d69a169 | |||
| 1730dd83bf | |||
| a3a3b492dd |
@@ -2,7 +2,7 @@ name: Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, build-test ]
|
||||
branches: [ master, stable, build-test ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
|
||||
@@ -292,6 +292,17 @@ public abstract class InsnArg extends Typed {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSameVar(SSAVar ssaVar) {
|
||||
if (ssaVar == null) {
|
||||
return false;
|
||||
}
|
||||
if (isRegister()) {
|
||||
SSAVar thisSsaVar = ((RegisterArg) this).getSVar();
|
||||
return Objects.equals(thisSsaVar, ssaVar);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSameCodeVar(RegisterArg arg) {
|
||||
if (arg == null) {
|
||||
return false;
|
||||
|
||||
+142
-19
@@ -23,6 +23,7 @@ import jadx.core.dex.instructions.ArithNode;
|
||||
import jadx.core.dex.instructions.ArithOp;
|
||||
import jadx.core.dex.instructions.IndexInsnNode;
|
||||
import jadx.core.dex.instructions.InsnType;
|
||||
import jadx.core.dex.instructions.InvokeNode;
|
||||
import jadx.core.dex.instructions.PhiInsn;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
@@ -32,6 +33,7 @@ import jadx.core.dex.instructions.args.RegisterArg;
|
||||
import jadx.core.dex.instructions.args.SSAVar;
|
||||
import jadx.core.dex.instructions.mods.TernaryInsn;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.IMethodDetails;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
@@ -70,6 +72,7 @@ public final class FixTypesVisitor extends AbstractVisitor {
|
||||
this.typeUpdate = root.getTypeUpdate();
|
||||
this.typeInference.init(root);
|
||||
this.resolvers = Arrays.asList(
|
||||
this::applyFieldType,
|
||||
this::tryRestoreTypeVarCasts,
|
||||
this::tryInsertCasts,
|
||||
this::tryDeduceTypes,
|
||||
@@ -292,6 +295,117 @@ public final class FixTypesVisitor extends AbstractVisitor {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use type for var assigned from field (IGET or SGET).
|
||||
* Insert additional casts at var use places.
|
||||
*/
|
||||
private Boolean applyFieldType(MethodNode mth) {
|
||||
try {
|
||||
boolean changed = false;
|
||||
// will add new SSA vars, can't use for-each loop
|
||||
List<SSAVar> sVars = mth.getSVars();
|
||||
for (int i = 0, varsCount = sVars.size(); i < varsCount; i++) {
|
||||
SSAVar ssaVar = sVars.get(i);
|
||||
if (tryFieldTypeWithNewCasts(mth, ssaVar, true)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
return false;
|
||||
}
|
||||
// rerun full type inference
|
||||
InitCodeVariables.rerun(mth);
|
||||
typeInference.initTypeBounds(mth);
|
||||
typeInference.runTypePropagation(mth);
|
||||
|
||||
// check if changed var types are fixed
|
||||
boolean success = true;
|
||||
for (SSAVar ssaVar : mth.getSVars()) {
|
||||
if (tryFieldTypeWithNewCasts(mth, ssaVar, false)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
typeInference.initTypeBounds(mth);
|
||||
typeInference.runTypePropagation(mth);
|
||||
mth.addWarnComment("Type inference incomplete: some casts might be missing");
|
||||
}
|
||||
return success;
|
||||
} catch (Exception e) {
|
||||
mth.addWarnComment("Type inference fix 'apply assigned field type' failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryFieldTypeWithNewCasts(MethodNode mth, SSAVar ssaVar, boolean insertCasts) {
|
||||
ArgType type = ssaVar.getTypeInfo().getType();
|
||||
if (type.isTypeKnown() || ssaVar.isTypeImmutable()) {
|
||||
return false;
|
||||
}
|
||||
InsnNode assignInsn = ssaVar.getAssignInsn();
|
||||
if (assignInsn == null) {
|
||||
return false;
|
||||
}
|
||||
InsnType insnType = assignInsn.getType();
|
||||
if (insnType != InsnType.IGET && insnType != InsnType.SGET) {
|
||||
return false;
|
||||
}
|
||||
ArgType fieldType = assignInsn.getResult().getInitType();
|
||||
// field type should be used
|
||||
if (insertCasts) {
|
||||
// try to find a use place and insert cast
|
||||
boolean inserted = false;
|
||||
for (RegisterArg useArg : ssaVar.getUseList()) {
|
||||
if (insertExplicitUseCast(mth, ssaVar, useArg, fieldType)) {
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
// force field type, will make type inference incomplete,
|
||||
// but it is better that completely unknown type
|
||||
ssaVar.setType(fieldType);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean insertExplicitUseCast(MethodNode mth, SSAVar ssaVar, RegisterArg useArg, ArgType fieldType) {
|
||||
InsnNode parentInsn = useArg.getParentInsn();
|
||||
if (!InsnUtils.isInsnType(parentInsn, InsnType.INVOKE)) {
|
||||
return false;
|
||||
}
|
||||
InvokeNode invoke = (InvokeNode) parentInsn;
|
||||
InsnArg instanceArg = invoke.getInstanceArg();
|
||||
if (instanceArg == null || !instanceArg.isSameVar(ssaVar)) {
|
||||
return false;
|
||||
}
|
||||
IMethodDetails details = mth.root().getMethodUtils().getMethodDetails(invoke);
|
||||
if (details == null) {
|
||||
return false;
|
||||
}
|
||||
int newCasts = 0;
|
||||
int k = -1;
|
||||
for (InsnArg invArg : invoke.getArgList()) {
|
||||
if (invArg == instanceArg) {
|
||||
continue;
|
||||
}
|
||||
k++;
|
||||
if (!invArg.isRegister()) {
|
||||
continue;
|
||||
}
|
||||
ArgType detailsArg = details.getArgTypes().get(k);
|
||||
ArgType invArgType = invArg.getType();
|
||||
ArgType resolvedType = mth.root().getTypeUtils().replaceClassGenerics(fieldType, invArgType, detailsArg);
|
||||
if (resolvedType != null && !resolvedType.equals(invArgType)) {
|
||||
IndexInsnNode castInsn = insertUseCast(mth, (RegisterArg) invArg, resolvedType);
|
||||
if (castInsn != null) {
|
||||
castInsn.add(AFlag.EXPLICIT_CAST);
|
||||
newCasts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newCasts > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix check casts to type var extend type:
|
||||
* <br>
|
||||
@@ -372,7 +486,9 @@ public final class FixTypesVisitor extends AbstractVisitor {
|
||||
&& !boundType.equals(var.getTypeInfo().getType())
|
||||
&& boundType.containsTypeVariable()
|
||||
&& !mth.root().getTypeUtils().containsUnknownTypeVar(mth, boundType)) {
|
||||
if (insertAssignCast(mth, var, boundType)) {
|
||||
IndexInsnNode castInsn = insertAssignCast(mth, var, boundType);
|
||||
if (castInsn != null) {
|
||||
castInsn.add(AFlag.SOFT_CAST);
|
||||
return 1;
|
||||
}
|
||||
return insertUseCasts(mth, var);
|
||||
@@ -388,58 +504,65 @@ public final class FixTypesVisitor extends AbstractVisitor {
|
||||
}
|
||||
int useCasts = 0;
|
||||
for (RegisterArg useReg : new ArrayList<>(useList)) {
|
||||
if (insertSoftUseCast(mth, useReg)) {
|
||||
IndexInsnNode castInsn = insertUseCast(mth, useReg, useReg.getInitType());
|
||||
if (castInsn != null) {
|
||||
castInsn.add(AFlag.SOFT_CAST);
|
||||
useCasts++;
|
||||
}
|
||||
}
|
||||
return useCasts;
|
||||
}
|
||||
|
||||
private boolean insertAssignCast(MethodNode mth, SSAVar var, ArgType castType) {
|
||||
private @Nullable IndexInsnNode insertAssignCast(MethodNode mth, SSAVar var, ArgType castType) {
|
||||
RegisterArg assignArg = var.getAssign();
|
||||
InsnNode assignInsn = assignArg.getParentInsn();
|
||||
if (assignInsn == null || assignInsn.getType() == InsnType.PHI) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
BlockNode assignBlock = BlockUtils.getBlockByInsn(mth, assignInsn);
|
||||
if (assignBlock == null) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
assignInsn.setResult(assignArg.duplicateWithNewSSAVar(mth));
|
||||
IndexInsnNode castInsn = makeSoftCastInsn(assignArg.duplicate(), assignInsn.getResult().duplicate(), castType);
|
||||
return BlockUtils.insertAfterInsn(assignBlock, assignInsn, castInsn);
|
||||
IndexInsnNode castInsn = makeCastInsn(assignArg.duplicate(), assignInsn.getResult().duplicate(), castType);
|
||||
if (!BlockUtils.insertAfterInsn(assignBlock, assignInsn, castInsn)) {
|
||||
return null;
|
||||
}
|
||||
return castInsn;
|
||||
}
|
||||
|
||||
private boolean insertSoftUseCast(MethodNode mth, RegisterArg useArg) {
|
||||
private @Nullable IndexInsnNode insertUseCast(MethodNode mth, RegisterArg useArg, ArgType castType) {
|
||||
InsnNode useInsn = useArg.getParentInsn();
|
||||
if (useInsn == null || useInsn.getType() == InsnType.PHI) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
if (useInsn.getType() == InsnType.IF && useInsn.getArg(1).isZeroConst()) {
|
||||
// cast isn't needed if compare with null
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
BlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn);
|
||||
if (useBlock == null) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
IndexInsnNode castInsn = makeSoftCastInsn(
|
||||
IndexInsnNode castInsn = makeCastInsn(
|
||||
useArg.duplicateWithNewSSAVar(mth),
|
||||
useArg.duplicate(),
|
||||
useArg.getInitType());
|
||||
castType);
|
||||
useInsn.replaceArg(useArg, castInsn.getResult().duplicate());
|
||||
boolean success = BlockUtils.insertBeforeInsn(useBlock, useInsn, castInsn);
|
||||
if (Consts.DEBUG_TYPE_INFERENCE && success) {
|
||||
LOG.info("Insert soft cast for {} before {} in {}", useArg, useInsn, useBlock);
|
||||
boolean inserted = BlockUtils.insertBeforeInsn(useBlock, useInsn, castInsn);
|
||||
if (!inserted) {
|
||||
return null;
|
||||
}
|
||||
return success;
|
||||
if (Consts.DEBUG_TYPE_INFERENCE) {
|
||||
LOG.info("Insert cast for {} before {} in {}", useArg, useInsn, useBlock);
|
||||
}
|
||||
return castInsn;
|
||||
}
|
||||
|
||||
private IndexInsnNode makeSoftCastInsn(RegisterArg result, RegisterArg arg, ArgType castType) {
|
||||
private IndexInsnNode makeCastInsn(RegisterArg result, RegisterArg arg, ArgType castType) {
|
||||
IndexInsnNode castInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);
|
||||
castInsn.setResult(result);
|
||||
castInsn.addArg(arg);
|
||||
castInsn.add(AFlag.SOFT_CAST);
|
||||
castInsn.add(AFlag.SYNTHETIC);
|
||||
return castInsn;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package jadx.tests.integration.types;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class TestTypeResolver26 extends IntegrationTest {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "checkstyle:IllegalType" })
|
||||
public static class TestCls {
|
||||
final ArrayList<String> target = new ArrayList<>();
|
||||
final ArrayList source = new ArrayList();
|
||||
|
||||
public void test() {
|
||||
((ArrayList) target).add(source.get(0)); // cast removed in bytecode
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
noDebugInfo();
|
||||
assertThat(getClassNode(TestCls.class))
|
||||
.code()
|
||||
.doesNotContain("type inference failed")
|
||||
.containsOne("this.target.add((String) this.source.get(0));");
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import jadx.core.utils.tasks.TaskExecutor;
|
||||
public class SilentTask extends CancelableBackgroundTask {
|
||||
private final Runnable task;
|
||||
|
||||
public SilentTask(Runnable task) {
|
||||
this.task = task;
|
||||
public SilentTask(Runnable backgroundTask) {
|
||||
this.task = backgroundTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -61,10 +61,15 @@ public class JadxProject {
|
||||
private transient boolean initial = true;
|
||||
private transient boolean saved;
|
||||
|
||||
private ProjectData data = new ProjectData();
|
||||
private final ProjectData data;
|
||||
|
||||
public JadxProject(MainWindow mainWindow) {
|
||||
this(mainWindow, new ProjectData());
|
||||
}
|
||||
|
||||
private JadxProject(MainWindow mainWindow, ProjectData projectData) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.data = Objects.requireNonNull(projectData);
|
||||
}
|
||||
|
||||
public void fillJadxArgs(JadxArgs jadxArgs) {
|
||||
@@ -312,16 +317,11 @@ public class JadxProject {
|
||||
}
|
||||
|
||||
public static JadxProject load(MainWindow mainWindow, Path path) {
|
||||
try {
|
||||
JadxProject project = new JadxProject(mainWindow);
|
||||
project.data = loadProjectData(path);
|
||||
project.saved = true;
|
||||
project.setProjectPath(path);
|
||||
return project;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error loading project", e);
|
||||
return null;
|
||||
}
|
||||
ProjectData projectData = loadProjectData(path);
|
||||
JadxProject project = new JadxProject(mainWindow, projectData);
|
||||
project.saved = true;
|
||||
project.setProjectPath(path);
|
||||
return project;
|
||||
}
|
||||
|
||||
public static ProjectData loadProjectData(Path path) {
|
||||
|
||||
@@ -551,17 +551,8 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private void openProject(Path path, Runnable onFinish) {
|
||||
LOG.debug("Loading project: {}", path);
|
||||
JadxProject jadxProject = JadxProject.load(this, path);
|
||||
if (jadxProject == null) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this,
|
||||
NLS.str("msg.project_error"),
|
||||
NLS.str("msg.project_error_title"),
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
jadxProject = new JadxProject(this);
|
||||
}
|
||||
project = JadxProject.load(this, path);
|
||||
settings.addRecentProject(path);
|
||||
project = jadxProject;
|
||||
loadFiles(onFinish);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import jadx.gui.ui.codearea.mode.JCodeMode;
|
||||
import jadx.gui.ui.panel.IViewStateSupport;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;
|
||||
|
||||
@@ -41,6 +42,7 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
private final transient JTabbedPane areaTabbedPane;
|
||||
|
||||
private boolean splitView = false;
|
||||
private final JCheckBox splitCheckboxNormal;
|
||||
|
||||
public ClassCodeContentPanel(TabbedPane panel, JClass jCls) {
|
||||
super(panel, jCls);
|
||||
@@ -48,26 +50,31 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
javaCodePanel = new CodePanel(new CodeArea(this, jCls));
|
||||
smaliCodePanel = new CodePanel(new SmaliArea(this, jCls));
|
||||
areaTabbedPane = buildTabbedPane(jCls, false);
|
||||
addCustomControls(areaTabbedPane);
|
||||
splitCheckboxNormal = addCustomControls(areaTabbedPane, false);
|
||||
|
||||
initView();
|
||||
javaCodePanel.load();
|
||||
initView(false);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
private void initView(boolean splitViewEnabled) {
|
||||
splitView = splitViewEnabled;
|
||||
removeAll();
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(0, 0, 0, 0));
|
||||
if (splitView) {
|
||||
JTabbedPane splitPaneView = buildTabbedPane(((JClass) node), true);
|
||||
if (splitViewEnabled) {
|
||||
JTabbedPane splitPaneView = buildTabbedPane((JClass) node, true);
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, areaTabbedPane, splitPaneView);
|
||||
add(splitPane);
|
||||
splitPane.setDividerLocation(0.5);
|
||||
splitPaneView.setSelectedIndex(1);
|
||||
UiUtils.uiRun(() -> {
|
||||
splitPane.setDividerLocation(0.5);
|
||||
splitPaneView.setSelectedIndex(1);
|
||||
});
|
||||
} else {
|
||||
splitCheckboxNormal.setSelected(false);
|
||||
add(areaTabbedPane);
|
||||
}
|
||||
invalidate();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private JTabbedPane buildTabbedPane(JClass jCls, boolean split) {
|
||||
@@ -92,11 +99,13 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
return areaTabbedPane;
|
||||
}
|
||||
|
||||
private void addCustomControls(JTabbedPane tabbedPane) {
|
||||
JCheckBox splitCheckBox = new JCheckBox("Split view", splitView);
|
||||
private JCheckBox addCustomControls(JTabbedPane tabbedPane, boolean splitCheckboxInitialState) {
|
||||
JCheckBox splitCheckBox = new JCheckBox("Split view", splitCheckboxInitialState);
|
||||
splitCheckBox.addItemListener(e -> {
|
||||
splitView = splitCheckBox.isSelected();
|
||||
this.initView();
|
||||
boolean newSplitView = splitCheckBox.isSelected();
|
||||
if (splitView != newSplitView) {
|
||||
this.initView(newSplitView);
|
||||
}
|
||||
});
|
||||
|
||||
JToolBar trailing = new JToolBar();
|
||||
@@ -106,6 +115,7 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
trailing.addSeparator(new Dimension(50, 1));
|
||||
trailing.add(splitCheckBox);
|
||||
tabbedPane.putClientProperty(TABBED_PANE_TRAILING_COMPONENT, trailing);
|
||||
return splitCheckBox;
|
||||
}
|
||||
|
||||
private void execInBackground(Runnable runnable) {
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class JadxTokenMaker extends JavaTokenMaker {
|
||||
private void processTokens(Token tokens) {
|
||||
Token prev = null;
|
||||
Token current = tokens;
|
||||
while (current != null) {
|
||||
while (current != null && current.getType() != TokenTypes.NULL) {
|
||||
if (prev != null) {
|
||||
switch (current.getType()) {
|
||||
case TokenTypes.RESERVED_WORD:
|
||||
|
||||
@@ -221,6 +221,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
}
|
||||
|
||||
private @Nullable ContentPanel showCode(JumpPosition jumpPos) {
|
||||
UiUtils.uiThreadGuard();
|
||||
JNode jumpNode = jumpPos.getNode();
|
||||
ContentPanel contentPanel = getTabByNode(jumpNode);
|
||||
if (contentPanel == null) {
|
||||
@@ -442,7 +443,9 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
@Override
|
||||
public void onTabCodeJump(TabBlueprint blueprint, @Nullable JumpPosition prevPos, JumpPosition position) {
|
||||
// queue task to wait completion of loading tasks
|
||||
mainWindow.getBackgroundExecutor().execute(new SilentTask(() -> showCode(position)));
|
||||
mainWindow.getBackgroundExecutor().execute(new SilentTask(() -> {
|
||||
UiUtils.uiRun(() -> showCode(position));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -379,9 +379,12 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static void errorMessage(Component parent, String message) {
|
||||
errorMessage(parent, NLS.str("message.errorTitle"), message);
|
||||
}
|
||||
|
||||
public static void errorMessage(Component parent, String title, String message) {
|
||||
LOG.error(message);
|
||||
JOptionPane.showMessageDialog(parent, message,
|
||||
NLS.str("message.errorTitle"), JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(parent, message, title, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
public static void copyToClipboard(String text) {
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=Die neue Sprache wird beim nächsten Start der Anwendung an
|
||||
msg.warning_title=Warnung
|
||||
msg.common_mouse_shortcut=Dies ist eine häufig verwendete Taste. Möchtest du sie wirklich an eine Aktion binden?
|
||||
msg.duplicate_shortcut=Der Tastenkürzel %s ist bereits in der Aktion „%s“ aus der Kategorie „%s“ festgelegt, fortfahren?
|
||||
msg.project_error_title=Fehler
|
||||
msg.project_error=Projekt konnte nicht geladen werden
|
||||
msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht.
|
||||
msg.cant_add_comment=Kann hier keinen Kommentar hinzufügen
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=New language will be displayed the next time application st
|
||||
msg.warning_title=Warning
|
||||
msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?
|
||||
msg.duplicate_shortcut=The shortcut %s is already set in action "%s" from category "%s", continue ?
|
||||
msg.project_error_title=Error
|
||||
msg.project_error=Project could not be loaded
|
||||
msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||
msg.cant_add_comment=Can't add comment here
|
||||
msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicac
|
||||
#msg.warning_title=Warning
|
||||
#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?
|
||||
#msg.duplicate_shortcut=The shortcut %s is already set in action "%s" from category "%s", continue ?
|
||||
#msg.project_error_title=Error
|
||||
#msg.project_error=Project could not be loaded
|
||||
#msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||
#msg.cant_add_comment=Can't add comment here
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=Bahasa baru akan ditampilkan saat aplikasi dimulai kembali.
|
||||
msg.warning_title=Peringatan
|
||||
msg.common_mouse_shortcut=Ini adalah tombol yang sering digunakan, apakah Anda yakin ingin mengaitkannya dengan tindakan?
|
||||
msg.duplicate_shortcut=Pintas %s sudah diatur pada tindakan "%s" dari kategori "%s", lanjutkan?
|
||||
msg.project_error_title=Kesalahan
|
||||
msg.project_error=Proyek tidak dapat dimuat
|
||||
msg.cmd_select_class_error=Gagal memilih kelas\n%s\nKelas tidak ada.
|
||||
msg.cant_add_comment=Tidak dapat menambahkan komentar di sini
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가
|
||||
#msg.warning_title=Warning
|
||||
#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?
|
||||
#msg.duplicate_shortcut=The shortcut %s is already set in action "%s" from category "%s", continue ?
|
||||
msg.project_error_title=오류
|
||||
msg.project_error=프로젝트를 로드 할 수 없습니다.
|
||||
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
|
||||
msg.cant_add_comment=여기에 주석을 추가할수 없음
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=Novo idioma será mostrado na próxima inicialização.
|
||||
#msg.warning_title=Warning
|
||||
#msg.common_mouse_shortcut=This is a commonly used key, are you sure you would like to bind it to an action?
|
||||
#msg.duplicate_shortcut=The shortcut %s is already set in action "%s" from category "%s", continue ?
|
||||
msg.project_error_title=Erro
|
||||
msg.project_error=Projeto não pôde ser carregado
|
||||
msg.cmd_select_class_error=Falha ao selecionar classe\n%s\nA classe não existe.
|
||||
msg.cant_add_comment=Não é possível adicionar comentários aqui
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=Новый язык применится при следу
|
||||
msg.warning_title=Внимание
|
||||
msg.common_mouse_shortcut=Это часто распространенная комбинация, вы действительно хотите назначить ее?
|
||||
msg.duplicate_shortcut=Действие %s уже выполняет "%s" в категории "%s", продолжить?
|
||||
msg.project_error_title=Ошибка
|
||||
msg.project_error=Проект не может быть загружен
|
||||
msg.cmd_select_class_error=Ошибка выбора класса\n%s\nЭтот класс не существует.
|
||||
msg.cant_add_comment=Невозможно добавить комментарий сюда
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=新的语言将在下次应用程序启动时显示。
|
||||
msg.warning_title=警告
|
||||
msg.common_mouse_shortcut=这是常用按键,您确定要绑定操作吗?
|
||||
msg.duplicate_shortcut=快捷键 %s 已设为操作 “%s”,位于群组 “%s”。确定要继续吗?
|
||||
msg.project_error_title=错误
|
||||
msg.project_error=项目无法加载
|
||||
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
|
||||
msg.cant_add_comment=无法在此添加注释
|
||||
msg.non_displayable_chars=部分字符在当前使用的字体“%s”中无法显示,是否显示这些字符?
|
||||
|
||||
@@ -346,8 +346,6 @@ msg.language_changed=新語言將於下次應用程式啟動時套用。
|
||||
msg.warning_title=警告
|
||||
msg.common_mouse_shortcut=這是常用按鍵,您確定要綁定操作嗎?
|
||||
msg.duplicate_shortcut=快捷鍵 %s 已設為操作 "%s",位於群組 "%s"。確定要繼續嗎?
|
||||
msg.project_error_title=錯誤
|
||||
msg.project_error=無法載入專案
|
||||
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
|
||||
msg.cant_add_comment=無法在此新增註解
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
|
||||
Reference in New Issue
Block a user