emsec / hal Goto Github PK
View Code? Open in Web Editor NEWHAL – The Hardware Analyzer
License: MIT License
HAL – The Hardware Analyzer
License: MIT License
Describe the bug
When parsing an incorrect VHDL netlist, with empty port maps of gates HAL crashes.
Expected behavior
hal should print an error and continue working.
Is your feature request related to a problem? Please describe.
Most other editors have an insert and an overwrite mode. This is currently not implemented in the HAL Python editor.
Describe the solution you'd like
Add a toggle between insert and overwrite mode bound to the [Insert] key.
Describe alternatives you've considered
None
Is your feature request related to a problem? Please describe.
Currently, when modifying a netlist from Python there is no easy way to undo that change.
Describe the solution you'd like
I suggest adding a method like netlist.reset()
(or "restore" or "reload", whatever fits your terminology) that discards all changes made to the netlist since loading, restoring it to its original state.
Describe alternatives you've considered
There could also be a button in the GUI for this.
netlist/netlist::is_module_in_netlist: only checks the id not the pointer. Therefore we can pass modules with the same id, that aren't part of the netlist anymore to get a false or pass a nullptr, which results in a segmentation fault.
Describe the bug
Both the Python console and the log have infinite scrollback. This causes high memory consumption of a few tens to hundreds additional MB when you leave HAL running for a long time (I like to have the tool in a VM and just hibernate that VM, i.e. HAL is never closed) or you print an insanely large amount of data from your Python script.
To Reproduce
top
and see the memory usage of the hal processExpected behavior
HAL should only keep the last few hundred lines of console and log loaded and visible. Older data should be removed from the widgets to save memory.
If you really, really want an infinite scrollback buffer you could put it on a disk and load it on demand / map it into memory.
Pressing [Ctrl+F] in the Python editor pops up a search bar at the bottom of the editor. That search bar is not automatically focused and does not seem to have any functionality, i.e. you can't search anything with it.
It is currently hard to see which module color belongs to what name. I suggest the following additions:
HAL sometimes selects two colors very close to each other when creating many modules, making the different modules hard to see. I suggest a manual override for that color by:
Is your feature request related to a problem? Please describe.
Gates in the navigation tree are currently sorted strictly by alphabetical order. This causes sort orders like this:
LUT_1 LUT_10 LUT_11 LUT2 LUT3 [...]
whereas the more "natural" sort order would be:
LUT_1 LUT2 LUT3 [...] LUT_10 LUT_11
Describe the solution you'd like
The sorting algorithm should detect numbers at the same place in the gate names and sort by number where applicable.
Describe alternatives you've considered
Maybe put a toggle for this in the settings?
Additional context
@devhoffmann Would you consider this for the "new graph views" project?
I'll try to eventually fix it, but don't know when I can come to this. So if anyone else has some time to spare, please feel free to take care of it.
From @adrian:
Das Schließen eines Dokuments bzw der gesamten Gui ist leider wesentlich aufwändiger als man vllt vermuten würde und setzt (zumindest für eine vollständige implementierung) einen haufen kram vorraus der aktuell schlichtweg noch nich da ist. Dabei geht es hauptsächlich um die speicherung des gesamten gui zustandes inklusive aller eventueller veränderungen an den daten, settings, layout und widgetinternen zuständen. Eine unvollständige implementierung die den ganzen kram übergeht, einfach alles löscht und die gui wieder in ihren startzustand versetzt wäre natürlich einfacher, falls sowas gewünscht ist, aber da kann man im grunde auch einfach das program schließen und neu starten, selbes Ergebniss
From @swallat:
Neue Idee: Ganzes Programm schließen, und automatisch neu starten.
Describe the bug
Writing an endless loop (by accident) will block the HAL user interface. There is currently no way to cancel a running script or console command besides killing HAL.
To Reproduce
while 1: pass
loop in the Python editorExpected behavior
The GUI should remain responsive and the run button should turn into a stop button to allow manual termination of the script.
The Python script seems to run on the GUI thread, which in turn blocks the Qt event pipeline and makes the GUI unresponsive. Instead, execution should take place in a separate worker thread.
Describe the bug
When opening a .vhd netlist for the first time and then saving it, HAL does not write to that file but creates a .hal project file. However, this file is not added to the recent files list in the startup view. Only the .vhd file is added.
A user who does not know that HAL projects work this way will most likely click the .vhd file from the recent files list. Of course, all changes to the netlist, all modules and all open scripts will not be there, so the user might think that HAL lost those changes.
To Reproduce
Expected behavior
The .hal file should also be added. There should, preferably, also be some kind of explanation that .hal files are projects created from .vhd files so the user knows which one to open.
See src/gui/python/python_editor.cpp:398
netlist/netlist_internal_manager::create_module: The module created by the create_module, may have a parrent owned by another netlist
Currently only whole nets can be selected and added to modules. This can cause cluttered module views.
Minimal example: In VHDL, create a small FSM from a FlipFlop and an inverter and then add 20 connections from the FF output to other gates. Now, try to add the nets connecting the FF and Inverter to a module. You will end up with a cluttered graph view with all the connections to other gates because they form one single net (because they are all fed from the same FF output pin).
I suggest adding an option to either splice nets or to add net parts to a module (in the form of "add this net to the module, but only the part to this destination"). This would de-clutter the graph view a lot, giving the reverse engineer the ability to focus better on the components of interest.
In src/netlist/gate.cpp and src/netlist/net.cpp we have still the operator<<
function. Since you can't reach them from other files (there is no declaration in the header), I would suggest to remove them. What do you think?
It just saves the content of the editor. Need to update editor content for reloading when saving the python editor. Suggestion save file path to continue work on file for the next start.
Is your feature request related to a problem? Please describe.
Opening more than one file in the Python editor can be tedious because the file chooser dialog must be used for each file separately. The file chooser also does not remember the last location, meaning the user must navigate to their files every time.
Describe the solution you'd like
The file chooser should allow selecting more than one Python script. If the user does this, one tab should open for each selected file.
Describe alternatives you've considered
It would also be nice if the file chooser remembered the last used folder.
Describe the bug
If we load a plugin, unload it and load it later again, the plugin_manager crashes with a segmentation fault in src/core/plugin_manager.cpp l. 163
To Reproduce
Steps to reproduce the behavior:
for example:
plugin_manager::load_all_plugins();
plugin_manager::unload_all_plugins();
plugin_manager::load_all_plugins(); // <- crashes with SIGSEGV
Expected behavior
It should not crash I guess ;P
Additional Information
The crash occurs in line 163 of src/core/plugin_manager.cpp:
...
std::set<std::string> dep_file_name = factory->get_dependencies();
...
I think the reason for the problem is, that the unload function deletes the static factory object of the corresponding gate library in line 245 (delete factory
). Since the object is defined as static, it will not be recreated in a second call of the get_factory()
function.
Of course, for the main usage you would not unload and load a library after, but in the context of testing it is necessary for me to have such a functionality ;)
For many tasks one needs to extract the truth table of a gate. Currently, the BDD must be extracted via a gate decorator and then evaluated to build the truth table.
However, the entire truth table is already present in the VHDL code in form of the init string of all LUT gates. I suggest exposing the init string as a property of each gate to simplify the process of truth table extraction.
Is your feature request related to a problem? Please describe.
If the GUI crashes all changes are lost.
The solution we will implement
We will add a feature that periodically saves to a "shadow file" which gets deleted upon clean exit.
If such a shadow file exists on load, hal suggests to restore the state from that file instead.
Related to issue #149
Currently, when closing the GUI we get an error. I think it has something to do with memory leaks from within the GUI. This also the problem for #7. We should make sure that we have correct memory management in the GUI following the Parent/Child paradigm.
Is your feature request related to a problem? Please describe.
After internal discussion it makes more sense that modules only contain gates, nets are handled implicitly.
The solution we will implement
We will revert back to the netlist handling all nets and only keep the gates inside modules.
In addition gates will now the module they are in for faster access.
netlist/module::set_name: does not check whether the passed name is the empty string. At the other hand, the create_module function (of the netlist) does not allow the empty string.
Describe the bug
In the Python editor, multiple tabs can be opened with the same file. This causes a similar issue as #139 because the editor does not synchronize those tabs. If you change something in one tab and save, then the other tab will not reflect this change. The user can thus easily overwrite their own file by accidentally opening it twice and forgetting which tab they worked in.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
There should never be more than one tab per file, meaning the editor should reject a request to open a file twice.
From Adrian:
GUI File Open Dialog Letzte Datei in Liste nicht lesbar
From @devhoffmann:
This seems to only happen on macOS (MUHAHAHAHAHA)
From Christian Kison:
The idea is to replace standard gates with their corresponding ANSI symbol.
E.g. https://de.wikipedia.org/wiki/Datei:Logic-gate-and-us.svg AND GATE
First of all this helps ASIC reversing. Secondly, this might turn out to be an good orientation-helper. Recognizing a place with like 5 XORS is easier, compared to seeing rectangles everywhere. (Its the same for the IDA graph view in software reversing.) Thirdly fastens reversing as "default" subcircuits are seen faster. No need to look for the type of every gate in the rectangle.
Describe the bug
After moving/deleting a .vhd or .hal file that is listed in the recent files widget and you try to open it via the recents file widgets again, hal opens a dialog that says it could not open the file, yet the item remains in the list. It is also currently not possible to remove an item from the list. It is possible that the list gets infinitely big.
To Reproduce
Expected behavior
Either ask wether the entry should be removed or stays, or for the simplicity, just remove the entry from the list.
After some time, HAL starts to consume 100% CPU on one core. Attaching a debugger yielded this call stack at the moment of stopping the offending thread.
libhal_gui.so!gui_graph_gate::paint(gui_graph_gate * const this, QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) (/home/rene/git/hal/src/gui/graph_layouter/gui_graph_gate.cpp:212)
libQt5Widgets.so.5![Unknown/Just-In-Time compiled code] (Unbekannte Quelle:0)
libQt5Widgets.so.5!QGraphicsView::paintEvent(QPaintEvent*) (Unbekannte Quelle:0)
libhal_gui.so!graph_layouter_view::paintEvent(graph_layouter_view * const this, QPaintEvent * event) (/home/rene/git/hal/src/gui/graph_layouter/graph_layouter_view.cpp:292)
libQt5Widgets.so.5!QWidget::event(QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QFrame::event(QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QGraphicsView::viewportEvent(QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplicationPrivate::notify_helper(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplication::notify(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplication::notifyInternal2(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::sendPaintEvent(QRegion const&) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice*, QList<QObject*> const&, int, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::drawWidget(QPaintDevice*, QRegion const&, QPoint const&, int, QPainter*, QWidgetBackingStore*) (Unbekannte Quelle:0)
libQt5Widgets.so.5![Unknown/Just-In-Time compiled code] (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidgetPrivate::syncBackingStore() (Unbekannte Quelle:0)
libQt5Widgets.so.5!QWidget::event(QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplicationPrivate::notify_helper(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplication::notify(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplication::notifyInternal2(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (Unbekannte Quelle:0)
libQt5Widgets.so.5![Unknown/Just-In-Time compiled code] (Unbekannte Quelle:0)
libQt5Core.so.5!QObject::event(QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QGraphicsScene::event(QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplicationPrivate::notify_helper(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Widgets.so.5!QApplication::notify(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplication::notifyInternal2(QObject*, QEvent*) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (Unbekannte Quelle:0)
libQt5Core.so.5![Unknown/Just-In-Time compiled code] (Unbekannte Quelle:0)
libglib-2.0.so.0!g_main_context_dispatch (Unbekannte Quelle:0)
libglib-2.0.so.0![Unknown/Just-In-Time compiled code] (Unbekannte Quelle:0)
libglib-2.0.so.0!g_main_context_iteration (Unbekannte Quelle:0)
libQt5Core.so.5!QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) (Unbekannte Quelle:0)
libQt5Core.so.5!QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) (Unbekannte Quelle:0)
libQt5Core.so.5!QCoreApplication::exec() (Unbekannte Quelle:0)
libhal_gui.so!plugin_gui::exec(plugin_gui * const this, program_arguments & args) (/home/rene/git/hal/src/gui/plugin_gui.cpp:129)
redirect_control_to_interactive_ui(const std::__cxx11::string & name, program_arguments & args) (/home/rene/git/hal/src/main.cpp:78)
main(int argc, const char ** argv) (/home/rene/git/hal/src/main.cpp:124)
List of issues found while testing:
netlist/netlist::is_module_in_netlist: only checks the id not the pointer. Therefore we can pass modules with the same id, that aren't part of the netlist anymore to get a false or pass a nullptr, which results in a segmentation fault.
netlist/module::set_name: does not check whether the passed name is the empty string. At the other hand, the create_module function (of the netlist) does not allow the empty string.
netlist/netlist_internal_manager::create_module: The module created by the create_module, may have a parrent owned by another netlist
In this code snippet:
import itertools
itertools
def test():
itertools
test()
The call inside the function throws a NameError. The same holds for accessing global variables. That includes functions, meaning you can't access a function from within a function.
Is your feature request related to a problem? Please describe.
When serializing to a HAL file, widgets do not know that (potential) changes have been serialized. This triggers misleading warning messages on exit.
The solution we will implement
There will be a global "state changed" mechanism that widgets can query and set.
Upon serialization it is cleared.
From Christian Kison:
Instances in verilog files like the example below (CLK_BUF_X3_0) are not parsed correctly, with our without the Parameters given. The PosX/PosY are meant for the layout to have a preplacement. I am coming from a placed and routed chip.
CLKBUF_X3
#(
.PosX(337060),
.PosY(870240)
)
CLKBUF_X3_0
(
.A(_tmp_2697),
.Z(_tmp_10493)
);
See NANDGATE_45NM.yaml
@christian Kison: We need test designs for that.
The Python class "module" contains the methods insert_gate
and add_net
. This is unintuitive, so I suggest renaming one of the two methods.
One can currently simply close the HAL main window with an unsaved netlist and/or unsaved python script, discarding those changes. There should be a warning message before actually quitting and discarding unsaved files.
Is your feature request related to a problem? Please describe.
When a project file (.hal) for an input netlist already exists, the GUI should offer loading this file instead.
Related to issue #147
Currently, arrow keys do nothing in the navigation view. For easier navigation, allow use of the up and down arrow keys.
Describe the bug
On a LUT6 you should set an init string this way:
lut.set_data("generic", "INIT", "bit_vector", "0"*16)
However, doing this also works, meaning you can set init strings of a wrong length:
lut.set_data("generic", "INIT", "bit_vector", "0"*4)
This causes all sorts of issues, including plugins that rely on correct config strings not working.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
HAL should not let the user do this if the init string clearly does not match the gate type.
Additional context
This works for other gates as well.
Describe the bug
The Python editor currently does not monitor the file on disk, meaning if you modify the file in another editor (or you have it in, say, a git repository and pull in some commits) and then click the save button in HAL, the external changes are lost.
Most IDEs can do this, so this will probably be unexpected for most users.
To Reproduce
Expected behavior
The editor must not overwrite a file that has been modified externally without asking for confirmation. The editor should display a subtle notification (no focus-stealing dialogs here) asking whether the user would like to re-load the file whenever HAL senses an external modification.
Additional context
gedit
does this well, maybe take a look there.
Describe the bug
If deserialization fails with all gate libraries when opening a file from the welcome screen, no error message is shown and the GUI simply stays on the welcome screen.
To Reproduce
Expected behavior
An error message should be displayed to the user.
Describe the bug
When we add a gate to HAL and don't connect its pins HAL writes an invalid netlist with an empty port map.
Expected behavior
Either write UNCONNECTED
as the assigned net or stop the writing altogether.
Additional context
Can occur in combination with #124
No autoscrolling when hitting enter repetitively in Python editor.
Is your feature request related to a problem? Please describe.
For large projects, if you want to quit HAL you need to click all Python tabs separately to save their changes. If you need to restart HAL often, then this is annoying.
Describe the solution you'd like
I suggest adding a save-all button to the Python editor that simply saves all tabs with unsaved changes at once.
Describe alternatives you've considered
We should discuss how we handle tabs that are new scripts, i.e. that have never been saved. If the user does this excessively, this could result in a lot of file chooser dialogs opening. Maybe do this and cancel the whole process if the user cancels one of the file choosers?
FDRE_0 : FDRE
generic map(
INIT => X"0"
)
port map (
C => signal_0,
CE => signal_104,
D => signal_12,
R => signal_103,
Q => signal_99
);
has to be... INIT => '0'
might be an error in the parser?
[29.03.2019 12:19:12] [core] [error] [/Users/eve/Uni/Projekte/hal/src/core/library_loader.cpp:44] cannot load library '/Users/eve/Uni/Projekte/hal/cmake-build-release/lib/plugins/graph_algorithm_py.so' (error: dlopen(/Users/eve/Uni/Projekte/hal/cmake-build-release/lib/plugins/graph_algorithm_py.so, 9): Symbol not found: __ZN22plugin_graph_algorithm13get_graph_cutENSt3__110shared_ptrI7netlistEENS1_I4gateEEjNS0_3setINS0_12basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEENS0_4lessISC_EENSA_ISC_EEEE
Referenced from: /Users/eve/Uni/Projekte/hal/cmake-build-release/lib/plugins/graph_algorithm_py.so
Expected in: flat namespace
in /Users/eve/Uni/Projekte/hal/cmake-build-release/lib/plugins/graph_algorithm_py.so) -- did you properly link the plugin?
Describe the bug
Calling input()
in either the Python console or editor locks up the GUI (see #101) and one can't type their input into the console as they would expect from the behavior of the standard python interactive shell. However, if HAL is started from a command line, typing something in that terminal and pressing [Enter] will pipe your input into the Python script.
To Reproduce
hal -g
input()
in the Python console or run it from the editorinput()
received whatever you typed on the command line.Expected behavior
A user will probably have some experience with the Python interactive shell and expect the HAL Python console to work the same way. Calling input()
should allow the user to type their input into the Python console (and not the system's terminal).
From @devhoffmann: To reproduce: import some python module via the code editor widget and call an included function run the snippet comment out the import statement run the snippet
Outcome: Although the module is not imported the second time, it is somehow still loaded by pybind and no error is thrown
@swallat: we should take a look at this
Suggestion for the dev working on the new graph view (if this is not already planned):
When working with large netlists where a functional unit (e.g. an FSM) is spread out across the entire graph, it is often hard to figure out how the components interact. I suggest adding a toggle "show only the selected module" that takes all gates and nets in the module (selected in the navigation list) and layouts them in a new, small graph.
This would allow for much easier visual inspection of the connections and you could increase the zoom level without something going off-screen.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.